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

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

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


当前回答

我同意PEZ和一些人的观点。

在我看来,“新”显然是自我描述的对象创建,而Greg Dean所描述的YUI模式完全被模糊了。

有人可能会写var bar = foo;或者var bar = baz();在baz不是对象的地方,创建方法似乎要危险得多。

其他回答

我认为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()方法。该方法解决了原型问题。

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

关于开关故障:

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

关于++和——:

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

关于新:

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

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

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

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

另一个新案例是我所说的Pooh Coding。我建议你顺应你所使用的语言,而不是反对它。

这种语言的维护者很有可能会根据他们鼓励使用的习语来优化语言。如果他们在语言中添加了一个新的关键字,他们可能认为在创建新实例时保持清晰是有意义的。

按照语言的意图编写的代码将在每个版本中提高效率。而避免语言关键结构的代码将随着时间的推移而遭受损失。

这远远超出了性能。我数不清我听过(或说过)多少次“他们为什么要这么做?”事实经常证明,在编写代码的时候,有一些“好的”理由。遵循语言之道是你的代码将来不会被嘲笑的最好保证。

情况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技术做了很多工作。他对语言关键要素的固执立场引发了许多有益的讨论。也就是说,有太多的人把每一个“坏”或“有害”的宣言都当作福音,拒绝超越一个人的观点。有时会让人有点沮丧。

使用new关键字提供的功能比从头构建每个对象有几个优点:

Prototype inheritance. While often looked at with a mix of suspicion and derision by those accustomed to class-based OO languages, JavaScript's native inheritance technique is a simple and surprisingly effective means of code re-use. And the new keyword is the canonical (and only available cross-platform) means of using it. Performance. This is a side-effect of #1: if I want to add 10 methods to every object I create, I could just write a creation function that manually assigns each method to each new object... Or, I could assign them to the creation function's prototype and use new to stamp out new objects. Not only is this faster (no code needed for each and every method on the prototype), it avoids ballooning each object with separate properties for each method. On slower machines (or especially, slower JS interpreters) when many objects are being created this can mean a significant savings in time and memory.

是的,new有一个关键的缺点,其他答案巧妙地描述了它:如果你忘记使用它,你的代码会毫无警告地崩溃。幸运的是,这个缺点很容易减轻——只需在函数本身添加一些代码:

function foo()
{
   // if user accidentally omits the new keyword, this will 
   // silently correct the problem...
   if ( !(this instanceof foo) )
      return new foo();
   
   // constructor logic follows...
}

现在你可以拥有new的优势,而不必担心误用造成的问题。

John Resig在他的“简单类”实例化文章中详细介绍了这种技术,以及在默认情况下将这种行为构建到“类”中的方法。绝对值得一读……他即将出版的新书《JavaScript忍者的秘密》(Secrets of the JavaScript Ninja)也是如此,这本书发现了JavaScript语言中隐藏的黄金和许多其他“有害”特性(关于with的章节对我们这些最初将这个备受诟病的特性视为噱头而不屑一顾的人特别有启发)。

通用的健全性检查

您甚至可以在检查中添加一个断言,如果您不愿意让坏掉的代码默默地工作的话。或者,正如一些人评论的那样,使用check来引入一个运行时异常:

if ( !(this instanceof arguments.callee) ) 
   throw new Error("Constructor called as a function");

请注意,这个代码片段能够避免硬编码构造函数名称,因为与前面的示例不同,它不需要实际实例化对象——因此,可以将它复制到每个目标函数中而无需修改。

ES5逃走

正如Sean McMillan, stephenbez和jrh所指出的,论点的使用。callee在ES5的严格模式下无效。因此,如果在该上下文中使用上述模式,它将抛出一个错误。

ES6和一个完全无害的新

ES6向JavaScript引入了类——不,不是像老派Crockford那样怪异地模仿java,而是在精神上更像他(和其他人)后来采用的轻量级方式,采用原型继承的最佳部分,并将公共模式烘焙到语言本身中。

...其中包括一个安全的新:

class foo
{
  constructor()
  {
    // constructor logic that will ONLY be hit 
    // if properly constructed via new
  } 
}

// bad invocation
foo(); // throws, 
// Uncaught TypeError: class constructors must be invoked with 'new'

但如果你不想用这种新糖怎么办?如果您只是想用上面所示的那种安全检查来更新您完美无缺的旧式原型代码,以便它们在严格模式下继续工作,该怎么办呢?

正如Nick Parsons指出的那样,ES6也提供了一个方便的检查,以new.target的形式:

function foo()
{
  if ( !(new.target) ) 
     throw new Error("Constructor called as a function");
   
  // constructor logic follows...
}

所以无论你选择哪种方法,只要稍加思考和保持卫生,你就可以无害地使用新产品。