【发布时间】:2015-05-27 15:59:27
【问题描述】:
我知道有类似的问题,但我想看看这些答案是否仍然有效,因为在新的 Javascript 引擎中进行了优化。
在我看来,在构造函数中定义函数的最大好处是您可以轻松避免必须知道 'this' 关键字的值:
var Person = function() {
var self = this;
self.firstName = null;
self.lastName = null;
self.fullName = function() {
return self.firstName + self.lastName;
};
};
Knockout Managing 'this' 推荐这种方法。这是一个很大的优势,尤其是当许多开发人员正在修改代码时,因为它非常易于理解和使用。
另一种方法是使用对象原型:
var Person = function() {
this.firstName = null;
this.lastName = null;
};
Person.prototype.fullName = function() {
return this.firstName + this.lastName;
};
在这种情况下有性能优势,因为函数对象将被创建一次。然而,我遇到的主要问题是处理“this”关键字可能很复杂。上面的例子很简单,但是如果你有事件处理程序、forEach 调用、jQuery each() 调用、从不同上下文调用的方法等等,很容易对 this 造成不好的利用。
当然,如果您了解“this”的工作原理并且知道方法是如何被调用的,那么您应该不会有太多问题。但是,根据我的经验,这需要时间并且容易出错,尤其是当代码由许多开发人员编写时。
我知道新的 JS 引擎,比如 V8,正在对通过创建隐藏类在构造函数中声明函数的情况进行优化:How the V8 engine works?。
所以我的问题是,考虑到新 JS 引擎完成的这些优化以及必须处理“this”关键字的复杂性,使用基于原型的方法是否仍然有意义?使用将所有内容都放入构造函数的方法我会丢失什么?
更新 1:
我刚刚在 Chrome(42 版)上做了一个微基准测试。我创建了 1M 个对象,其中包含构造函数中的函数和原型中的函数。这是一个非常简单的对象,有两个变量和三个函数,结果如下:
Functions inside constructor: 1.91 seconds
Functions in prototype: 1.10 seconds
听起来即使在 V8 中进行了这些优化,它仍然快 73%。然而,这是一个微基准。不确定这是否会对现实世界的应用程序产生重大影响。
更新 2:
我还查看了内存消耗,也存在很大差异。对于构造函数内部的函数:
Shallow size: 64,000,120
Retained size: 336,001,128
对于原型函数:
Shallow size: 40,000,000
Retained size: 40,000,000
隐藏类的优化不是那么好,或者我错过了一些东西。我正在使用 V8 建议的单态代码(没有 args 的构造函数),但不确定我是否做错了什么。
更新 3:
这是我所做的测试的链接,以防有人指出其中的错误:http://jsperf.com/dg-constructor-vs-prototype
【问题讨论】:
-
FWIW,我添加了第三个测试用例,“POJO”不使用原型或“new”关键字,它只是创建一个具有公共方法和私有变量的对象。它在性能上与构造函数示例相当。无论哪种方式,原型方法总是更快,尽管增加了复杂性。 jsperf.com/dg-constructor-vs-prototype/2
-
谢谢斯科特。是的,听起来目前最有效的方法是使用原型。很遗憾,因为我喜欢将 'this' 分配给 'self' 的简单性,并确保 'self' 指向对象,而不必考虑 'this' 指向什么。
-
是的,我完全同意。对于简单的对象(没有继承),使用
prototype无非是一种微优化,缺点是必须仔细理解和管理this的使用。
标签: javascript