在另一个question中,用户指出new
关键字使用起来很危险,并提出了一个不使用new
的对象创建解决方案。我不相信这是真的,主要是因为我使用了Prototype,Scriptaculous和其他优秀的JavaScript库,并且每个人都使用new
关键字。
尽管如此,昨天我正在观看道格拉斯·克罗克福德在YUI剧院的演讲,他说的完全一样,他的代码中不再使用new
关键字(Crockford on JavaScript - Act III: Function the Ultimate - 50:23 minutes)
使用new
关键字是否“不好”?使用它的优点和缺点是什么?
答案 0 :(得分:592)
Crockford为推广优秀的JavaScript技术做了很多工作。他对语言关键要素的看法立场引发了许多有益的讨论。也就是说,有太多的人把每一个“坏”或“有害”的宣言当作福音,拒绝超越一个人的意见。有时可能会有点令人沮丧。
使用new
关键字提供的功能比从头构建每个对象有几个好处:
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
的优势,而不必担心意外误用造成的问题。你甚至可以在检查中添加一个断言,如果破坏代码的思想默默地工作困扰你。或者,如some所述,使用检查引入运行时异常:
if ( !(this instanceof arguments.callee) )
throw new Error("Constructor called as a function");
(请注意,此代码段能够避免对构造函数名称进行硬编码,因为与前面的示例不同,它不需要实际实例化对象 - 因此,可以将其复制到每个目标函数中而无需修改。)< / p>
John Resig在他的Simple "Class" Instantiation帖子中详细介绍了这项技术,并在默认情况下包含了将此行为构建到“类”中的方法。绝对值得一读...就像他即将出版的书Secrets of the JavaScript Ninja一样,它在JavaScript语言的这个和许多其他“有害”特征中找到了隐藏的金色({strong>章节 {{1}对于我们这些最初将这种备受诟病的特征视为噱头的人来说,尤其具有启发性。)
答案 1 :(得分:178)
我刚刚阅读了他的Crockfords书中的一些部分“Javascript:The Good Parts”。我觉得他认为一切都让他感到有害:
关于开关坠落:
我从不允许开关盒落下 直到下一个案例。我曾经发现过 我的代码中的一个错误 意外跌倒了 在发表了激烈的演讲之后 关于为什么有时会跌倒的原因 有用。 (第97页,ISBN 978-0-596-51774-8)
关于++和 -
++(增量)和 - (减量) 运营商已为人所知 通过鼓励来弥补不良代码 过分狡猾。他们是第二名 只有错误的建筑 启用病毒和其他安全性 恫吓。 (第122页)
关于新的:
如果您忘记加入 new 调用构造函数时的前缀 功能,那么这个将不会 绑定到新对象。可悲的是,这个 将被绑定到全局对象,所以 而不是增加你的新对象, 你会破坏全球化 变量。那真是太糟糕了。那里 没有编译警告,也没有 运行时警告。 (第49页)
还有更多,但我希望你能得到这张照片。
我对你的问题的回答:不,这不是有害的。但是如果你忘了使用它,你可能会遇到一些问题。如果你在良好的环境中发展,你会发现它。
<强>更新强>
写完这个答案大约一年后,第五版ECMAScript发布了,支持strict mode。在严格模式下,this
不再绑定到全局对象,而是绑定到undefined
。
答案 2 :(得分:91)
Javascript是动态语言,有很多方法可以搞砸另一种语言会阻止你。
避免使用new
这样的基本语言功能,因为你可能会陷入困境,有点像在穿过雷区之前移除闪亮的新鞋,以防万一你的鞋子变得泥泞。
我使用一种约定,其中函数名称以小写字母开头,实际类别定义的“函数”以大写字母开头。结果是一个非常引人注目的视觉线索,即“语法”错误: -
var o = MyClass(); // this is clearly wrong.
除此之外,良好的命名习惯也有帮助。在所有函数执行之后,因此名称中应该有一个动词,而类代表对象,是没有动词的名词和形容词。
var o = chair() // Executing chair is daft.
var o = createChair() // makes sense.
有趣的是SO的语法着色如何解释上面的代码。
答案 3 :(得分:42)
我是Javascript的新手,所以也许我只是没有太多的经验来提供一个很好的观点。但我想分享我对这个“新”事物的看法。
我来自C#世界,使用关键字“new”是如此自然,以至于工厂设计模式对我来说很奇怪。
当我第一次使用Javascript编写代码时,我没有意识到存在像“YUI模式”中的“new”关键字和代码,并且我不会花费很长时间来遇到灾难。在回顾我编写的代码时,我忘记了特定行应该做的事情。更混乱的是当我“干运行”代码时,我的思维无法真正在对象实例边界之间转换。
然后,我找到了“new”关键字给我,它“分开”了。使用new关键字,它会创建一些东西。没有new关键字,我知道我不会把它与创建事物混淆,除非我调用的函数给了我强有力的线索。
例如,对于var bar=foo();
我没有任何线索,因为它可能是什么吧......它是一个返回值还是一个新创建的对象?但是var bar = new foo();
我确定bar是一个对象。
答案 4 :(得分:39)
另一个案例 new是我称之为Pooh Coding的案例。小熊维尼跟着他的肚子。我说使用您正在使用的语言,而不是反对。
有可能语言的维护者会优化他们试图鼓励的习语的语言。如果他们在语言中添加了新的关键字,他们可能认为在创建新实例时要明确是有意义的。
按照语言的意图编写的代码将在每次发布时提高效率。避免语言关键结构的代码会随着时间的推移而受到影响。
编辑:这远远超出了性能。我无法计算我听过(或说过)“为什么他们做了那个?”的时间。找到奇怪的代码。事实证明,在编写代码时,有一些“好”的理由。遵循语言道是你最好的保险,因为从现在开始几年没有你的代码被嘲笑。
答案 5 :(得分:24)
我写了一篇关于如何在没有new关键字的情况下缓解调用构造函数问题的帖子
它主要是教学,但它显示了如何创建使用或不使用new
的构造函数,并且不要求您在每个构造函数中添加boilerplate code来测试this
。
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);
答案 6 :(得分:22)
不使用new关键字背后的理由很简单:
完全没有使用它,你可以避免意外遗漏它带来的陷阱。 YUI使用的构造模式是一个如何完全避免使用新关键字的示例“
var foo = function () {
var pub= { };
return pub;
}
var bar = foo();
或者你可以这样:
function foo() { }
var bar = new foo();
但是这样做可能会导致某人忘记使用新关键字,并且此运算符都是fubar。 AFAIK这样做没有任何好处(除了你已经习惯了)。
在今天结束时:这是关于防守。你能使用新的声明吗?是。它会使您的代码更危险吗?是。
如果您曾经编写过C ++,那么删除它们之后就像将指针设置为NULL一样。
答案 7 :(得分:20)
我认为“新”会增加代码的清晰度。清晰度值得一切。很高兴知道存在陷阱,但是通过避免清晰来避免这些陷阱似乎不适合我。
答案 8 :(得分:15)
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
new
是必需的,否则您将收到错误new Date().getFullYear(); // correct, returns the current year, i.e. 2010
Date().getFullYear(); // invalid, returns an error
答案 9 :(得分:12)
以下是我对使用new
运算符支持和反对的两个最强有力论据的最简要总结:
new
new
运营商可能会造成灾难性后果
如果他们不正确的影响
作为普通函数调用。一个
在这种情况下函数的代码会
在范围内执行
函数被调用,而不是在
本地对象的范围为
意。这可能导致全球化
要获得的变量和属性
用灾难性的东西覆盖
后果。function Func()
,
然后拨打Func.prototype
并添加东西,以便你
可以调用new Func()
来构建
你的对象看起来有些难看
程序员,谁宁愿使用
另一种对象继承方式
用于建筑和风格
的原因。有关此论点的更多信息,请查看Douglas Crockford的精彩简洁书籍Javascript:The Good Parts。事实上无论如何要检查它。
new
new
运算符
原型分配很快。有关此技术的简单解释,请参阅John Resig's post,并对他提倡的继承模型进行更深入的解释。
答案 10 :(得分:9)
我同意pez和一些人的意见。
我觉得“新”是自描述对象创建,GUI Dean描述的YUI模式是完全模糊的。
有人可以写var bar = foo;
或var bar = baz();
的可能性,其中baz不是对象创建方法,似乎远更危险。
答案 11 :(得分:8)
我认为新事物是邪恶的,不是因为如果你忘记错误地使用它可能会导致问题,但是因为它搞砸了继承链,使语言难以理解。
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()方法。该方法解决了原型问题。
答案 12 :(得分:0)
IMNSHO“new”是 2021 JavaScript 中的一个有缺陷的概念。它在不需要的地方添加了单词。它使函数/构造函数的返回值隐式并强制在函数/构造函数中使用 this。在代码中添加噪音从来都不是一件好事。
// With new
function Point(x, y) {
this.x = x
this.y = y
}
let point = new Point(0,0)
对比
// Without new
function Point(x, y) {
return { x, y }
}
let point = Point(0,0)