在另一个问题中,一个用户指出使用new关键字是危险的,并提出了一个不使用new的对象创建解决方案。我不相信这是真的,主要是因为我使用过Prototype、Script.aculo.us和其他优秀的JavaScript库,它们都使用new关键字。

尽管如此,昨天我在YUI剧院看了Douglas Crockford的演讲,他说了完全相同的事情,他不再在他的代码中使用new关键字(Crockford谈JavaScript -第三幕:函数终极- 50:23分钟)。

使用new关键字“不好”吗?使用它的优点和缺点是什么?


当前回答

我写了一篇关于如何缓解不使用new关键字调用构造函数的问题的文章。

它主要是说教性的,但它展示了如何创建使用或不使用new的构造函数,并且不需要在每个构造函数中添加样板代码来测试它。

不使用new的构造函数

以下是该技巧的要点:

/**
 * Wraps the passed in constructor so it works with
 * or without the new keyword
 * @param {Function} realCtor The constructor function.
 *    Note that this is going to be wrapped
 *    and should not be used directly
 */
function ctor(realCtor) {
  // This is going to be the actual constructor
  return function wrapperCtor() {
    var obj; // The object that will be created
    if (this instanceof wrapperCtor) {
      // Called with new
      obj = this;
    } else {
      // Called without new. Create an empty object of the
      // correct type without running that constructor
      surrogateCtor.prototype = wrapperCtor.prototype;
      obj = new surrogateCtor();
    }
    // Call the real constructor function
    realCtor.apply(obj, arguments);
    return obj;
  }

  function surrogateCtor() {}
}

下面是如何使用它:

// Create our point constructor
Point = ctor(function(x, y) {
  this.x = x;
  this.y = y;
});

// This is good
var pt = new Point(20, 30);
// This is OK also
var pt2 = Point(20, 30);

其他回答

以下是我对支持和反对使用新运算符的两个最有力的论点所做的最简短的总结:

反对新建的理由

Functions designed to be instantiated as objects using the new operator can have disastrous effects if they are incorrectly invoked as normal functions. A function's code in such a case will be executed in the scope where the function is called, instead of in the scope of a local object as intended. This can cause global variables and properties to get overwritten with disastrous consequences. Finally, writing function Func(), and then calling Func.prototype and adding stuff to it so that you can call new Func() to construct your object seems ugly to some programmers, who would rather use another style of object inheritance for architectural and stylistic reasons.

关于这个争论的更多信息,请参阅Douglas Crockford的著作《JavaScript: The Good Parts》。事实上,还是去看看吧。

赞成新

使用新的运算符 原型赋值很快。 那些关于意外的东西 运行一个构造函数 全局名称空间中的代码可以 很容易预防,如果你总是 包含一些代码在您的 要检查的构造函数 看看它们是否被调用 正确,而且,在这种情况下 他们没有,接听电话 如你所愿。

请参阅John Resig的文章,以获得对这种技术的简单解释,以及对他所倡导的继承模型的一般更深入的解释。

情况1:new不是必需的,应该避免使用

var str = new String('asd');  // type: object
var str = String('asd');      // type: string

var num = new Number(12);     // type: object
var num = Number(12);         // type: number

情况2:new是必需的,否则您将得到一个错误

new Date().getFullYear();     // correct, returns the current year, i.e. 2010
Date().getFullYear();         // invalid, returns an error

我刚刚读了Crockford的书《JavaScript: The Good parts》的一部分。我有一种感觉,他认为凡是咬过他的东西都是有害的:

关于开关故障:

我从不允许开关箱掉下来 进入下一个案例。我曾经发现 我的代码中有一个bug,是由 无意中立刻落空 在做了一次有力的演讲之后 为什么有时候会失败 有用的。(第97页,ISBN 978-0-596-51774-8)

关于++和——:

++(递增)和——(递减) 运营商已经知道 通过鼓励来促成糟糕的代码 过度的诡计多端。他们是第二 只有错误的架构在 启用病毒和其他安全性 威胁。(第122页)

关于新:

如果你忘了说新的 调用构造函数时的前缀 函数,那么这个就不是 绑定到新对象。遗憾的是,这 将被绑定到全局对象,那么 不是扩大你的新对象, 你将在全球范围内大肆宣传 变量。这真的很糟糕。在那里 是否没有编译警告,并且没有 运行时警告。(49页)

还有更多,但我希望你能理解。

对于你的问题,我的回答是:不,它是无害的。但如果你忘记使用它时,你可能会有一些问题。如果你在一个良好的环境中发展,你会注意到这一点。

在ECMAScript的第5版中,支持严格模式。在严格模式下,this不再绑定到全局对象,而是绑定到undefined。

我认为new是邪恶的,不是因为如果你错误地忘记使用它,它可能会引起问题,而是因为它破坏了继承链,使语言更难理解。

JavaScript是基于原型的面向对象的。因此,每个对象都必须像这样从另一个对象创建:var newObj= object .create(oldObj)。这里oldObj被称为newObj的原型(因此是“基于原型的”)。这意味着如果一个属性在newObj中没有找到,那么它将在oldObj中被搜索。默认情况下,newObj将是一个空对象,但由于它的原型链,它似乎拥有oldObj的所有值。

另一方面,如果执行var newObj=new oldObj(),则newObj的原型是oldObj。原型,这是很难理解的。

诀窍在于使用

Object.create=function(proto){
  var F = function(){};
  F.prototype = proto;
  var instance = new F();
  return instance;
};

它在这个函数中,并且只有在这里才应该使用new。在此之后,只需使用Object.create()方法。该方法解决了原型问题。

我写了一篇关于如何缓解不使用new关键字调用构造函数的问题的文章。

它主要是说教性的,但它展示了如何创建使用或不使用new的构造函数,并且不需要在每个构造函数中添加样板代码来测试它。

不使用new的构造函数

以下是该技巧的要点:

/**
 * Wraps the passed in constructor so it works with
 * or without the new keyword
 * @param {Function} realCtor The constructor function.
 *    Note that this is going to be wrapped
 *    and should not be used directly
 */
function ctor(realCtor) {
  // This is going to be the actual constructor
  return function wrapperCtor() {
    var obj; // The object that will be created
    if (this instanceof wrapperCtor) {
      // Called with new
      obj = this;
    } else {
      // Called without new. Create an empty object of the
      // correct type without running that constructor
      surrogateCtor.prototype = wrapperCtor.prototype;
      obj = new surrogateCtor();
    }
    // Call the real constructor function
    realCtor.apply(obj, arguments);
    return obj;
  }

  function surrogateCtor() {}
}

下面是如何使用它:

// Create our point constructor
Point = ctor(function(x, y) {
  this.x = x;
  this.y = y;
});

// This is good
var pt = new Point(20, 30);
// This is OK also
var pt2 = Point(20, 30);