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

IT技术 javascript
2021-02-04 07:36:18

在另一个问题中,一位用户指出该new关键字使用起来很危险,并提出了一个未使用的对象创建解决方案new我不相信这是真的,主要是因为我使用过 Prototype、Scriptaculous 和其他优秀的 JavaScript 库,而且每个人都使用了new关键字。

尽管如此,昨天我在 YUI 剧院观看道格拉斯·克罗克福德 (Douglas Crockford) 的演讲,他说了完全相同的话,他new不再在他的代码中使用关键字(Crockford on JavaScript - 第三幕:终极功能 - 50:23分钟)。

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

6个回答

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

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

  1. 原型继承尽管习惯于基于类的面向对象语言的人经常带着怀疑和嘲笑的眼光看待 JavaScript,但 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而不必担心因意外误用而引起的问题。您甚至可以在检查中添加断言,如果您认为代码损坏的静默工作困扰着您。或者,正如某些人所评论的,使用检查来引入运行时异常:

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

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

John Resig 在他的Simple“Class”Instantiation帖子中详细介绍了这种技术,并在默认情况下包含了一种将此行为构建到“类”中的方法。绝对值得一读……就像他即将出版的书一样,JavaScript Ninja 的秘密,它在 JavaScript 语言的这个特性和许多其他“有害”特性中找到了隐藏的金子(这一with我们这些最初不屑一顾的人特别有启发性)这个备受诟病的功能是一个噱头)。

if (!(this instanceof arguments.callee)) throw Error("Constructor called as a function");// 更通用,不需要知道构造函数名称,让用户修复代码。
2021-03-18 07:36:18
如果您担心性能 - 实际上有理由担心 - 那么根本不要进行检查。要么记住使用new,要么使用包装函数为您记住它。怀疑,如果您正处于重要时刻,那么您已经用尽了其他优化,例如记忆化,因此无论如何您的创建调用都将被本地化......
2021-03-23 07:36:18
如果我拼错了一个标识符,我的代码就会中断。我不应该使用标识符吗?不使用,new因为您可能会忘记在代码中编写它,这与我的示例一样可笑。
2021-03-28 07:36:18
使用arguments.callee检查,如果你已经调用new可能不是那么好,因为arguments.callee不严格模式下可用。最好使用函数名。
2021-03-30 07:36:18
如果你开启了严格模式,如果你忘记使用 new 你尝试使用这个时会得到一个异常:yuiblog.com/blog/2010/12/14/strict-mode-is-coming-to-town那就是比向每个构造函数添加一个检查实例更容易。
2021-04-05 07:36:18

我刚刚阅读了他在 Crockfords 的书“Javascript: The Good Parts”中的一些部分。我有一种感觉,他认为曾经咬过他的一切都是有害的:

关于开关故障:

我从不允许 switch case 进入下一个 case。我曾经在我的代码中发现了一个错误,这是因为我在做了一次关于为什么跌倒有时有用的有力演讲后立即出现了意外跌倒。(第 97 页,ISBN 978-0-596-51774-8)

关于 ++ 和 --

众所周知,++(增量)和 --(减量)运算符会通过鼓励过度的技巧而导致不良代码。在引发病毒和其他安全威胁方面,它们仅次于有缺陷的架构。(第 122 页)

关于新:

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

还有更多,但我希望你能看到图片。

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

更新

在编写此答案大约一年后,ECMAScript 的第 5 版发布,支持严格模式在严格模式下,this不再绑定到全局对象而是绑定到undefined.

我完全同意。解决方案:始终记录用户必须如何实例化您的对象。使用示例,用户可能会剪切/粘贴。每种语言中都有可能被误用导致异常/意外行为的构造/功能。它不会使它们有害。
2021-03-20 07:36:18
++、-- 也是如此。他们用最清晰的语言准确表达了我的意图。我爱他们!一些人可能很清楚切换失败,但我对这些感到厌倦。当它们显然更清晰时,我会使用它们(因为双关语,无法抗拒)。
2021-03-24 07:36:18
有一个约定,构造函数总是以大写字母开头,所有其他函数以小写字母开头。
2021-03-29 07:36:18
我刚刚意识到 Crockford 不认为 WHILE 有害......我不知道我创建了多少次不定式循环,因为我忘记了增加一个变量......
2021-03-29 07:36:18
我对 Crockford 的回答是:编程很难,我们去购物吧。嘘!
2021-04-02 07:36:18

Javascript 是动态语言,有无数种方法可以在另一种语言阻止你的地方搞砸。

避免使用基本的语言功能,例如new在您可能会搞砸的基础上,有点像在穿过雷区之前脱掉闪亮的新鞋,以防您的鞋子弄脏。

我使用一个约定,其中函数名称以小写字母开头,而实际上是类定义的“函数”以大写字母开头。结果是一个非常引人注目的视觉线索,表明“语法”是错误的:-

var o = MyClass();  // this is clearly wrong.

除此之外,良好的命名习惯也有帮助。毕竟函数做事,因此它的名字中应该有一个动词,而类代表对象,是没有动词的名词和形容词。

var o = chair() // Executing chair is daft.
var o = createChair() // makes sense.

SO 的语法着色如何解释上面的代码很有趣。

有一次我忘记输入“功能”这个词。应该不惜一切代价避免这个词。还有一次我没有在多行 if/then 语句中使用花括号。所以现在我不使用花括号,只写一行条件。
2021-03-19 07:36:18
“这显然是错误的”为什么?对于我的课程(只是这样做if (! this instanceof MyClass) return new MyClass()),我实际上更喜欢new-less 语法。为什么?因为函数构造函数更通用省略new可以很容易地将构造函数更改为常规函数......或者反过来。添加new使代码比它需要的更具体。因此不太灵活。与 Java 相比,建议接受List而不是ArrayList调用者可以选择实现。
2021-03-26 07:36:18
是的,我只是对语法着色有同样的想法。
2021-03-30 07:36:18

我是 Javascript 的新手,所以也许我在提供一个好的观点方面没有太多经验。但我想分享我对这个“新”事物的看法。

我来自 C# 世界,在那里使用关键字“new”非常自然,以至于我觉得工厂设计模式很奇怪。

当我第一次用 Javascript 编写代码时,我没有意识到存在“new”关键字和 YUI 模式中的代码,而且我很快就会遇到灾难。在回顾我编写的代码时,我忘记了特定行应该做什么。更混乱的是,当我“空运行”代码时,我的思想无法真正在对象实例边界之间转换。

然后,我找到了“new”关键字,它对我来说是“分离”的东西。使用 new 关键字,它可以创建事物。如果没有 new 关键字,我知道我不会将它与创建事物混淆,除非我正在调用的函数为我提供了强有力的线索。

例如,var bar=foo();我不知道 bar 可能是什么……它是返回值还是新创建的对象?var bar = new foo();我确信 bar 是一个对象。

同意,我相信工厂模式应该遵循像 makeFoo() 这样的命名约定
2021-03-12 07:36:18
@JoshuaRamirez 重点不在于typeof new foo() == "object"new返回 的实例foo,并且您知道可以调用foo.bar()foo.bang()但是,可以通过使用JsDoc 的 @return轻松缓解这种情况,而不是我提倡使用程序代码(避免使用这个词new
2021-03-16 07:36:18
+1 - “新”的存在给出了更清晰的意图陈述。
2021-03-28 07:36:18
这种响应有点奇怪,因为 JS 中的几乎所有东西都是一个对象。当所有函数都是对象时,为什么你需要确定一个函数是一个对象?
2021-04-04 07:36:18
@JuanMendes 嗯...你的帖子让我觉得你喜欢 new 因为它在你的代码库中的显式关键字。我可以挖那个。这就是我使用module模式的原因。我将有一个名为 createFoo 或 NewFoo 或 MakeFoo 的函数,只要它是明确的就没有关系。在里面,我声明了用作函数返回的对象文字中的闭包的变量。该对象文字最终成为您的对象,而该函数只是一个构造函数。
2021-04-04 07:36:18

另一种情况新就是我所说的维尼编码小熊维尼跟着他的肚子。我说去你正在使用,而不是语言反对它。

语言的维护者可能会针对他们试图鼓励的习语优化语言。如果他们在语言中添加了一个新关键字,他们可能认为在创建新实例时明确是有意义的。

遵循语言意图编写的代码将在每次发布时提高效率。避免语言关键结构的代码会随着时间的推移而受到影响。

编辑:这远远超出了性能。我不能指望我听说的时候(或者说)“到底为什么他们做?” 当发现奇怪的代码时。事实证明,在编写代码时有一些“好的”理由。遵循语言之道是您的代码在几年后不被嘲笑的最佳保障。

+1 为 Pooh Coding 链接 - 现在我需要找借口将其应用到我的对话中......
2021-03-12 07:36:18
不知道您是否会看到此评论,但 Pooh Coding 链接已失效。
2021-03-12 07:36:18
哈哈!我在那个特定领域有多年的经验,我可以向你保证你不会发现任何困难。他们经常在 robowiki.net 上叫我 Pooh。=)
2021-03-13 07:36:18
谢谢你的提醒。我们上周将 robowiki.net 迁移到了一个新的 wiki,目前无法访问旧内容。我会尽快让那些旧链接工作。
2021-04-02 07:36:18