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

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

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


当前回答

我认为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关键字的理由很简单:

通过根本不使用它,您可以避免意外遗漏它的陷阱。YUI使用的构造模式是一个如何完全避免new关键字的例子:

var foo = function () {
    var pub = { };
    return pub;
}
var bar = foo();

或者,你可以这样做:

function foo() { }
var bar = new foo();

但是这样做有可能会有人忘记使用new关键字,并且this操作符完全是FUBAR。据我所知,这样做没有任何好处(除了你已经习惯了)。

在一天结束的时候:这是关于防御性的。你会用这个新语句吗?是的。这会让你的代码更危险吗?是的。

如果你曾经写过c++,这类似于在删除指针后将它们设置为NULL。

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

我是JavaScript的新手,所以可能我只是没有太多的经验来提供一个好的观点。然而,我想分享一下我对这个“新”事物的看法。

我来自c#世界,在那里使用关键字“new”是如此自然,以至于工厂设计模式对我来说看起来很奇怪。

当我第一次用JavaScript编码时,我没有意识到有“new”关键字和像YUI模式那样的代码,很快我就陷入了灾难。当回顾我所编写的代码时,我不知道某一行应该做什么。更混乱的是,当我“干运行”代码时,我的思维不能真正地在对象实例边界之间转换。

然后,我发现“新”关键字,对我来说,“分离”的东西。使用new关键字,它创建东西。如果没有new关键字,我知道我不会把它与创建东西混淆,除非我调用的函数提供了强有力的线索。

例如,用var bar=foo();我没有任何线索,什么酒吧可能是....它是一个返回值还是一个新创建的对象?但是用var bar = new foo();我确定bar是一个对象。

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...
}

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

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

关于开关故障:

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

关于++和——:

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

关于新:

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

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

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

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