JavaScript 的“new”关键字是否被认为是有害的?

新手上路,请多包涵

另一个问题 中,用户指出 new 关键字使用起来很危险,并提出了一种不使用 new 的对象创建解决方案。我不相信那是真的,主要是因为我使用了 PrototypeScript.aculo.us 和其他优秀的 JavaScript 库,并且他们每个人都使用了 new 关键字。

尽管如此,昨天我在 YUI 剧院观看了 Douglas Crockford 的演讲,他说了完全相同的话,他没有在他的代码中使用 new 关键字( Crockford on JavaScript - Act III:功能终极 - 50:23 分钟)。

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

原文由 Pablo Fernandez 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 370
2 个回答

Crockford 为推广优秀的 JavaScript 技术做了很多工作。他对语言关键要素的固执己见引发了许多有益的讨论。也就是说,有太多人将每一个“坏”或“有害”的声明都当作福音,拒绝超越一个人的意见。有时可能会有点令人沮丧。

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

  1. 原型继承。尽管习惯于使用基于类的 OO 语言的人常常以怀疑和嘲笑的混合眼光看待 JavaScript 的本机继承技术,但它是一种简单且非常有效的代码重用方法。而 new 关键字是使用它的规范(并且唯一可用的跨平台)方式。
  2. 表现。这是#1 的副作用:如果我想为我创建的每个对象添加 10 个方法,我 可以 只编写一个创建函数,手动将每个方法分配给每个新对象……或者,我可以将它们分配给创建函数的 prototype 并使用 new 消除新对象。这不仅速度更快(原型上的每个方法都不需要代码),它还避免了为每个方法使用单独的属性来膨胀每个对象。在较慢的机器上(或者尤其是较慢的 JS 解释器),当创建许多对象时,这可能意味着时间和内存的显着节省。

是的, 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 忍者的秘密》 一样,它在这个和 JavaScript 语言的许多其他“有害”特性中发现了隐藏的黄金(关于 with章节 特别有启发性对于我们这些最初将这个饱受诟病的功能视为噱头的人来说)。

通用完整性检查

您甚至可以在检查中添加一个断言,如果您认为损坏的代码会默默地工作,这会困扰您。或者,正如 一些人 评论的那样,使用检查来引入运行时异常:

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

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

ES5带走

正如 Sean McMillanstephenbezjrh 所指出的,使用 arguments.callee 在 ES5 的 严格模式下 是无效的。因此,如果您在该上下文中使用上述模式,将会抛出错误。

ES6 和一个完全无害的 new

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

…其中一部分包括一个保险箱 new

 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 而不会造成伤害。

原文由 Shog9 发布,翻译遵循 CC BY-SA 4.0 许可协议

我刚刚阅读了 Crockford 的书“ JavaScript: The Good Parts ”的部分内容。我觉得他认为曾经咬过他的一切都是有害的:

关于开关掉线:

我从不允许切换案例落到下一个案例。我曾经在我的代码中发现了一个错误,该错误是在就为什么失败有时有用发表了激烈的演讲后立即由意外失败引起的。 (第 97 页,ISBN 978-0-596-51774-8)

关于 ++ 和 –:

众所周知,++(递增)和 –(递减)运算符会鼓励过多的技巧,从而导致错误的代码。在启用病毒和其他安全威胁方面,它们仅次于错误的架构。 (第 122 页)

关于新品:

如果在调用构造函数时忘记包含 new 前缀,那么 this 将不会绑定到新对象。遗憾的是, 将绑定到全局对象,因此您不会扩充新对象,而是会破坏全局变量。那真的很糟糕。没有编译警告,也没有运行时警告。 (第 49 页)

还有更多,但我希望你能明白。

我对你问题的回答: 不,它没有害处。 但是如果你忘记了应该使用它,你可能会遇到一些问题。如果您在良好的环境中进行开发,您会注意到这一点。

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

原文由 some 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题