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

在另一个问题中 ,用户指出, new关键字是危险的使用,并提出了一个解决scheme,没有使用new对象创build。 我不相信这是真的,主要是因为我使用了Prototype,Scriptaculous和其他优秀的JavaScript库,每个人都使用new关键字。

尽pipe如此,昨天我在YUI剧院观看道格拉斯·克罗克福德的讲话,他说完全一样的东西,他没有在他的代码中再使用new关键字( Crockford on JavaScript – Act III:Function the Ultimate – 50: 23分钟 )。

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

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

使用new关键字提供的function与从零开始构build每个对象相比有几个优点:

  1. 原型inheritance 。 虽然习惯于基于类的面向对象语言的人常常被怀疑和嘲笑混合在一起,JavaScript的本地inheritance技术是一种简单且令人惊讶的有效的代码重用方法。 而新的关键字是使用它的规范(也是唯一可用的跨平台)手段。
  2. 性能。 这是#1的一个副作用:如果我想为每个创build的对象添加10个方法,我可以只写一个创build函数,手动将每个方法分配给每个新的对象…或者,我可以将它们分配给创build函数的prototype并用new来消除新的对象。 这不仅更快(原型上的每一个方法都不需要代码),它避免了每个方法的每个对象具有不同的属性。 在较慢的机器上(尤其是较慢的JS解释器),当创build多个对象时,这可能意味着显着节省时间和内存。

是的, 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的优点,而不必担心由于意外误用造成的问题。 你甚至可以添加一个断言的检查,如果想破碎的代码默默工作困扰你。 或者,如一些评论,使用检查来引入运行时exception:

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

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

John Resig在他简单的“类”实例化过程中详细介绍了这种技术,以及默认情况下将这种行为构build到“类”中的方法。 绝对值得一读…就像他即将出版的书“JavaScript忍者的秘密”一样 ,它在JavaScript语言的许多其他“有害”特征中发现了隐藏的黄金(对于那些最初解雇这个惹人注目的function作为噱头)。

我刚刚读了他的Crockfords书“Javascript:好部分”的一些部分。 我感觉到他认为所有曾经把他咬过的东西都是有害的:

关于切换通过:

我绝不允许开关柜落到下一个柜子。 有一次,我在代码中发现了一个错误,这个错误是由于意外跌落而发生的。 (第97页,ISBN 978-0-596-51774-8)

关于++和 –

众所周知,++(增量)和(减量)运算符通过鼓励恶意代码来促成糟糕的代码。 在启用病毒和其他安全威胁方面,它们仅次于错误的架构。 (第122页)

关于新的:

如果您在调用构造函数时忘记包含新的前缀,则不会绑定到新对象。 不幸的是, 将被绑定到全局对象,所以不是扩大你的新对象,而是全局variables。 这真的很糟糕。 没有编译警告,并且没有运行时警告。 (第49页)

还有更多,但我希望你能得到这张照片。

我回答你的问题: 不,这不是有害的。 但如果你忘记使用它,当你应该可以有一些问题。 如果你在良好的环境中发展,你会注意到这一点。

更新

在答复写了大约一年后,第五版ECMAScript发布了,支持严格模式 。 在严格模式下, this不再局限于全局对象,而是undefined

Javascript是一种dynamic语言,有很多方法可以让另一种语言阻止你。

避免基本的语言function,如new的基础上,你可能会搞砸了有点像删除你的shiny新鞋穿行前雷区,以防万一你可能会让你的鞋子泥泞。

我使用一个约定,其中函数名以小写字母开头,实际上类定义的“函数”以大写字母开头。 结果是一个非常令人信服的视觉线索,“语法”是错误的: –

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

在这个良好的命名习惯帮助之上。 在所有的function都做了事情之后,因此它的名字中应该有一个动词,而类代表对象,而且是没有动词的名词和形容词。

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

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

我是新手到Javascript,所以也许我只是不太经验,提供一个很好的观点。 然而,我想分享一下我对这个“新”事物的看法。

我来自C#世界,在那里使用关键字“new”是如此自然,这是工厂devise模式,看起来很奇怪。

当我第一次使用JavaScript代码的时候,我并没有意识到YUI模式中存在“新”关键字和代码,并且没有太长的时间。 当我回顾我写的代码时,我忽略了某一行应该做什么。 更混乱的是,当我“干运行”代码时,我的思维不能真正在对象实例边界之间传递。

然后,我find了“新”关键字,这对我来说,它“分离”的东西。 使用新的关键字,它创造了一些东西。 没有新的关键字,我知道我不会混淆创造的东西,除非我所调用的function给我强大的线索。

例如,用var bar=foo(); 我没有什么线索,可能是什么吧….这是一个返回值还是新创build的对象? 但是, var bar = new foo(); 我知道吧,酒吧是一个对象。

新的另一个案例是我称之为维尼编码 。 小熊维尼跟着他的肚子。 我说的是你正在使用的语言,而不是反对它。

语言的维护者可能会优化他们试图鼓励的成语的语言。 如果他们把一个新的关键字放入语言中,他们可能认为在创build一个新的实例时应该很清楚。

按照该语言的意图编写的代码将在每个版本中提高效率。 而避免语言的关键结构的代码将会随着时间而受到影响。

编辑:这远远超出了性能。 我听不懂(或者说)他们为什么这么做? 当发现奇怪的代码。 通常情况下,编写代码时有一些“好”的理由。 遵循语言的道路是你今后几年不用嘲笑你的代码的最好保证。

不使用新关键字的基本原理很简单:

通过不使用它,你可以避免意外忽略它的陷阱。 YUI使用的构造模式是一个如何避免新关键字的例子“

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

或者你可以这样做:

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

但是通过这样做,你会冒着忘记使用关键字的风险,而这个操作符都是fubar。 AFAIK没有这样做的好处(除了你习惯它)。

在一天结束时: 这是关于防守。 你可以使用新的陈述吗? 是。 它会让你的代码更危险吗? 是。

如果你曾经写过C ++,那么在删除它们之后,将指针设置为NULL。

我写了一篇关于如何减轻调用构造函数而不使用new关键字的问题。
它主要是教学法,但它显示了如何创build可以使用或不使用new构造函数,并且不要求您添加样板代码以在每个构造函数中testingthis构造函数。

http://js-bits.blogspot.com/2010/08/constructors-without-using-new.html

这是技术的要点:

 /** * Wraps the passed in constructor so it works with * or without the new keyword * @param {Function} realCtor The constructor function. * Note that this is going to be wrapped * and should not be used directly */ function ctor(realCtor){ // This is going to be the actual constructor return function wrapperCtor(){ var obj; // object that will be created if (this instanceof wrapperCtor) { // Called with new obj = this; } else { // Called without new. Create an empty object of the // correct type without running that constructor surrogateCtor.prototype = wrapperCtor.prototype; obj = new surrogateCtor(); } // Call the real constructor function realCtor.apply(obj, arguments); return obj; } function surrogateCtor() {} } 

以下是如何使用它:

 // Create our point constructor Point = ctor(function(x,y){ this.x = x; this.y = y; }); // This is good var pt = new Point(20,30); // This is OK also var pt2 = Point(20,30); 

我认为“新”增加了代码的清晰度。 清晰度是值得的一切。 很高兴知道有缺陷,但通过避免清楚避免它们似乎不是我的方式。

案例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, ie 2010 Date().getFullYear(); // invalid, returns an error 

以下是我可以用两个最强的论点来反驳使用new算子的最简短的总结:

反对new论点

  1. 被devise为使用new运算符实例化为对象的函数,如果作为普通函数被错误调用,可能会产生灾难性的影响。 在这种情况下,函数的代码将在调用该函数的作用域中执行,而不是按照预期在本地对象的作用域中执行。 这可能会导致全局variables和属性被覆盖,造成灾难性的后果。
  2. 最后,编写function Func() ,然后调用Func.prototype并添加东西给它,这样你可以调用new Func()来构造你的对象,这对于一些程序员来说似乎是丑陋的,他们宁愿使用另一种风格的对象inheritance来构build和文体的原因。

有关这个论点的更多信息,请查阅道格拉斯·克罗克福德(Douglas Crockford)的“伟大而简洁的书”Javascript:The Good Parts。 事实上,无论如何检查出来。

有利于new论点

  1. 使用new操作符和原型分配是很快的。
  2. 如果在构造函数的函数中总是包含一些代码来检查它们是否被正确调用,那么在全局名称空间中意外运行构造函数的代码的事情可以很容易地被阻止,并且在它们不是,根据需要适当地处理呼叫。

请参阅John Resig的文章 ,对这种技术进行简单的解释,并对他所倡导的inheritance模式进行更深入的解释。

我同意佩兹和这里的一些。

在我看来,“新”是自我描述性的对象创作,格雷格·迪恩(Greg Dean)描述的YUI模式完全被遮蔽了

有人可能写var bar = foo;的可能性var bar = foo;var bar = baz(); 巴兹不是一个对象创build方法似乎更危险。

我认为新的是邪恶的,不是因为如果你忘记误用它,可能会导致问题,但是因为它把inheritance链搞砸了,使得语言变得更加难以理解。

JavaScript是基于原型的面向对象。 因此,每个对象必须从另一个对象创build,比如var newObj=Object.create(oldObj) 。 这里oldObj被称为newObj的原型(因此是“基于原型的”)。 这意味着如果在newObj中找不到属性,那么它将在oldObj中被search。 newObj默认是一个空对象,但是由于它的原型链,它看起来具有oldObj的所有值。

另一方面,如果你做var newObj=new oldObj()newObj的原型是oldObj.prototype ,这是不必要的难以理解的。

诀窍就是使用

 Object.create=function(proto){ var F = function(){}; F.prototype = proto; var instance = new F(); return instance; }; 

它在这个函数里面,只有在这里才能使用。 之后,只需使用Object.create()方法。 该方法解决了原型问题。