JavaScript's &;new"关键字是否有害?

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

尽管如此,昨天我在YUI剧院看了Douglas Crockford的演讲,他说了完全相同的事情,他不再在代码中使用new关键字(Crockford讲JavaScript -第三幕:终极函数- 50:23分钟)。

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

117019 次浏览

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

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

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

或者,你可以这样做:

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

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

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

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

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

关于开关故障:

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

关于++和——:

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

关于新:

如果你忘记包含new 调用构造函数时的前缀 函数,那么将不是 绑定到新对象。可悲的是,# EYZ0 将被绑定到全局对象,那么 不是扩大你的新对象, 你将在全球范围内大肆宣传 变量。这真的很糟糕。在那里 是否没有编译警告,并且没有 运行时警告。(49页)< / p >

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

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

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

我认为“新”;增加代码的清晰度。而清晰就是一切。知道有陷阱是好事,但通过避免清晰来避免陷阱似乎不适合我。

JavaScript是一种动态语言,有无数种方法可以把其他语言阻止你的事情搞砸。

避免使用像new这样的基本语言功能,因为你可能会搞砸,这有点像在穿过雷区之前脱掉你闪亮的新鞋,以防你的鞋沾上了泥。

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

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

最重要的是,良好的命名习惯会有所帮助。毕竟,函数是做事情的,因此它的名字中应该有一个动词,而类代表对象,是没有任何动词的名词和形容词。

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

Stack Overflow的语法着色是如何解释上面的代码的,这很有趣。

我同意与沥青和这里的一些人。

在我看来,“new"是自描述对象创建,Greg Dean描述的YUI模式是完全被遮挡

有人可能会写var bar = foo;var bar = baz();,其中巴兹不是一个对象创建方法,似乎更危险。

Crockford为推广优秀的JavaScript技术做了很多工作。他对语言关键要素的固执立场引发了许多有益的讨论。也就是说,有太多的人把每一个“坏”的宣言都看成是“坏”的。或“;harmful"就像福音一样,拒绝超越一个人的观点。有时会让人有点沮丧。

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

  1. # EYZ1。尽管习惯于基于类的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在他的简单的“Class"实例化帖子中详细介绍了这个技巧,同时也包括了一种将这种行为构建到你的“类”中的方法。默认情况下。绝对值得一读……正如他即将出版的书,JavaScript忍者的秘密,发现隐藏的黄金在这和许多其他“有害”;JavaScript语言的特性(在with上的对于我们这些最初将这个饱受诟病的特性视为噱头而不屑一顾的人来说尤其具有启发性)。

通用的健全性检查

您甚至可以在检查中添加一个断言,如果您不愿意让坏掉的代码默默地工作的话。或者,像一些注释的那样,使用check来引入一个运行时异常:

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

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

ES5拿走了

正如肖恩·麦克米兰stephenbezjrh所指出的,在ES5的严格模式中使用arguments.callee是无效的。因此,如果在该上下文中使用上述模式,它将抛出一个错误。

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'

但如果你不想要使用新的糖呢?如果您只是想用上面所示的那种安全检查来更新您完美无缺的旧式原型代码,以便它们在严格模式下继续工作,该怎么办呢?

好吧,和尼克·帕森斯笔记一样,ES6也提供了一个方便的检查,以new.target的形式:

function foo()
{
if ( !(new.target) )
throw new Error("Constructor called as a function");
   

// constructor logic follows...
}

因此,无论您选择哪种方法,只要稍加思考并保持良好的卫生习惯,您就可以在没有伤害的情况下使用new

另一个新案例是我所说的EYZ1。小熊维尼跟着肚子走。我说去你正在使用的语言,而不是反对

这种语言的维护者很有可能会根据他们鼓励使用的习语来优化语言。如果他们在语言中添加了一个新的关键字,他们可能认为在创建新实例时保持清晰是有意义的。

按照语言的意图编写的代码将在每个版本中提高效率。而避免语言关键结构的代码将随着时间的推移而遭受损失。

编辑:这远远超出了性能。我数不清我听过(或说过)多少次当发现奇怪的代码时,“他们为什么要用?”事实经常证明,在编写代码的时候,有一些“好的”理由。遵循语言之道是你的代码将来不会被嘲笑的最好保证。

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

我来自c#世界,在那里使用关键字“new”是如此自然,以至于工厂设计模式对我来说看起来很奇怪。

当我第一次用Javascript编码时,我没有意识到有“new”关键字和像YUI模式那样的代码,很快我就陷入了灾难。当回顾我所编写的代码时,我不知道某一行应该做什么。更混乱的是,当我“干运行”代码时,我的思维不能真正地在对象实例边界之间转换。

然后,我发现“新”关键字对我来说,它“分离”的东西。使用new关键字,它创建东西。如果没有new关键字,我知道我不会把它与创建东西混淆,除非我调用的函数提供了强有力的线索。

例如,对于var bar=foo();,我没有线索,因为什么酒吧可能是....它是一个返回值还是一个新创建的对象?但是对于var bar = new foo();,我确定bar是一个对象。

我认为new是邪恶的,不是因为如果你错误地忘记使用它,它可能会引起问题,而是因为它破坏了继承链,使语言更难理解。

JavaScript是基于原型的面向对象的。因此,每个对象都必须从另一个对象创建,如var newObj=Object.create(oldObj)。这里oldObj被称为newObj的原型(因此是“基于原型的”)。这意味着如果一个属性在newObj中没有找到,那么它将在oldObj中被搜索。默认情况下,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;
};

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

情况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, i.e. 2010
Date().getFullYear();         // invalid, returns an error

下面是我对支持和反对使用new操作符的两个最有力的论点所做的最简短的总结:

反对new的论点

  1. 设计为 实例化为对象,使用 new操作符可能有灾难性的 效果,如果他们是错误的 作为普通函数调用。一个 函数的代码在这种情况下会 的范围内执行 函数被调用,而不是in 局部对象的作用域为 的意图。这会导致全局 获取的变量和属性 覆盖灾难性的 李的后果。< / > 最后,写入function Func(), 然后调用Func.prototype 然后加一些东西进去,这样你 可以调用new Func()来构造 你的目标在某些人看来很难看 程序员,谁宁愿用 另一种类型的对象继承 建筑和风格 李原因。< / >

关于这个争论的更多信息,请参阅Douglas Crockford的著作《Javascript: The Good Parts》。不管怎样,还是去看看吧。

支持new的参数

    使用new操作符和 原型赋值很快。 那东西是偶然的 运行一个构造函数 全局名称空间中的代码可以 很容易预防,如果你总是 包含一些代码在您的 要检查的构造函数 看看它们是否被调用 正确,而且,在这种情况下 他们没有,接听电话

请参阅约翰·瑞西格的文章以获得对这种技术的简单解释,以及对他所倡导的继承模型的一般更深入的解释。

我写了一篇关于如何缓解不使用new关键字调用构造函数的问题的文章 它主要是说教性的,但它展示了如何创建使用或不使用new的构造函数,并且不需要在每个构造函数中添加样板代码来测试this。< / p >

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);

IMNSHO“new"在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)