在另一个问题中,一个用户指出使用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不是对象的地方,创建方法似乎要危险得多。

其他回答

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

关于开关故障:

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

关于++和——:

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

关于新:

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

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

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

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

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

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

不使用new关键字的理由很简单:

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

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

或者,你可以这样做:

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

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

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

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

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

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

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

在我看来,在2021年的JavaScript中,“新”是一个有缺陷的概念。它在不需要的地方添加单词。它使函数/构造函数的返回值为隐式,并强制在函数/构造函数中使用此返回值。在代码中添加噪声从来都不是一件好事。

// With new
function Point(x, y) {
    this.x = x
    this.y = y
}
let point = new Point(0, 0)

Vs.

// Without new
function Point(x, y) {
    return { x, y }
}
let point = Point(0, 0)