Crockford做了很多工作来普及良好的JavaScript技术。他在语言的关键要素上持有自己的观点,引发了许多有用的讨论。尽管如此,仍有太多人将每个“不好”或“有害”的声明视为真理,拒绝超越一个人的意见。有时这可能会有点令人沮丧。
使用
new
关键字提供的功能比从头开始构建每个对象具有几个优点:
- 原型继承。虽然那些习惯于基于类的面向对象语言的人经常被怀疑和嘲笑,但JavaScript的本地继承技术是一种简单而令人惊讶的有效的代码重用手段。而
new
关键字是使用它的规范(也是唯一可用的跨平台)方式。
- 性能。这是#1的副作用:如果我想给我创建的每个对象添加10个方法,我可以编写一个创建函数,手动将每个方法分配给每个新对象... 或者,我可以将它们分配给创建函数的
prototype
并使用new
来生成新对象。这不仅更快(无需为原型上的每个方法编写每个方法的代码),而且避免了为每个方法分别创建单独属性的对象膨胀。在较慢的机器(或特别是较慢的JS解释器)上,当创建许多对象时,这可能意味着节省大量时间和内存。
是的,new
有一个关键的缺点,正如其他答案所描述的那样:如果你忘记使用它,你的代码将在没有警告的情况下崩溃。幸运的是,这个缺点很容易被缓解 - 只需在函数本身中添加一些代码:
function foo()
{
if ( !(this instanceof foo) )
return new foo();
}
现在你可以享受`new`的优势,而不必担心因意外误用而导致的问题。
John Resig在他的
Simple "Class" Instantiation文章中详细介绍了这种技术,并包括一种将此行为默认构建到您的“类”中的方法。绝对值得一读……就像他即将出版的书
Secrets of the JavaScript Ninja一样,在JavaScript语言的许多其他“有害”功能中发现了隐藏的黄金(关于
with
的章节对于那些最初将此功能视为噱头的人特别启发)。
一个通用的健全性检查
如果你担心破碎的代码会静默地工作,甚至可以添加一个断言来进行检查。或者,正如
some所评论的那样,使用检查来引入运行时异常:
if ( !(this instanceof arguments.callee) )
throw new Error("Constructor called as a function");
请注意,这个代码片段可以避免硬编码构造函数名称,因为与前面的例子不同,它没有实际实例化对象的需要——因此,它可以被复制到每个目标函数中而无需修改。
ES5 的限制
正如
Sean McMillan、
stephenbez 和
jrh 所指出的那样,在 ES5 的
strict mode 中使用
arguments.callee
是无效的。因此,如果在该上下文中使用它,上述模式将抛出错误。
ES6 和完全无害的 new
ES6 在 JavaScript 中引入了
Classes - 不是以旧式 Crockford 那种奇怪的 Java 模仿方式,而是更像他(和其他人)后来采用的轻量级方式,将原型继承的最佳部分融入语言本身,并将常见模式固化到语言中。
...其中一部分包括安全的 new
操作:
class foo
{
constructor()
{
}
}
foo();
但是,如果你不想使用新的语法糖怎么办?如果你只是想更新你完全正常的原型式代码,并加入上述安全检查以使其在严格模式下继续工作呢?
好吧,正如Nick Parsons指出,ES6还提供了一个方便的检查方式,即new.target
:
function foo()
{
if ( !(new.target) )
throw new Error("Constructor called as a function");
}
所以,无论您选择哪种方法,只要思考清楚并保持良好的卫生习惯,您就可以毫不担心地使用new
。
var myDataSource = new Y.DataSource.IO({source:"./myScript.php"});
。 - aditya_gaurnew
不危险。省略new
才是危险的,因此是不好的。但在 ES5 中,你可以使用 严格模式,它可以保护你免受这种危险和其他许多危险的影响。 - jkdevthis
不会是globalThis
,但这不仅仅是这样:类根本不能在没有new
的情况下使用。自ES6以来,语言已经发展到鼓励使用new
,就像较新的构造函数例如Map
或Set
不能再(不带)使用new
一样。违反这一点似乎是不合理的。请注意,旧的构造函数,例如new String
、new Date
、new Array
,具有旧规则。 - Sebastian Simon