我知道有类似的问题,但我希望看看这些答案在新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'是如何工作的并且知道如何调用方法,那么您应该没有太多问题。但是,根据我的经验,这需要时间并且容易出错,尤其是当代码由许多开发人员制作时。
我知道新的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
答案 0 :(得分:3)
我进行了快速测试。如果在构造函数中声明函数,则即使在优化之后,两个对象实例也具有不同的函数实例。但是对于原型,您只有一个函数实例来解释性能差异。
var Person = function () {
var self = this;
self.firstName = null;
self.lastName = null;
self.fullName = function () {
return self.firstName + self.lastName;
};
};
Person.prototype.fullName2 = function () {
return this.firstName + this.lastName;
};
var a = new Person();
var b = new Person();
console.log(a.fullName == b.fullName); // returns false
console.log(a.fullName2 == b.fullName2); // returns true
答案 1 :(得分:0)
我一直在使用一种略有不同的方法,IMO具有清晰性的优点,并且避免了每次调用构造函数时都重新创建成员函数:
例如:
// Constructor for MyClass
function MyClass(a, b){
// set properties of the instance from constructor arguments
this.a = a;
this.b = b;
// assign the report function as a member of this instance
this.report = Report;
}
// Report function is defined at the module level,
// but used by assigning it to an instance variable
// within the constructor.
function Report(){
console.log( "a=" + this.a, "b=" + this.b);
}
只有一个成员函数实例可以由同一类的所有实例共享(就像将函数分配给class.prototype.function
时的情况一样),因此这种方法非常有效,并且具有额外的优点,IMO:
用法:
// Create instances of my class
var inst1 = new MyClass( "as", "df");
var inst2 = new MyClass( "gh", "jk");
// Report the instances
inst1.report();
inst2.report();
// Class and method declarations follow below here...
是否有任何缺点这种方法?
答案 2 :(得分:0)
就像@Ersin Basaran提到的那样,在构造函数内部创建的函数对于每个对象实例都是唯一的,与使用原型创建时使每个对象实例的功能相同的函数不同。
但是,在ES6(ECMAScript2015)中引入类之后,如果使用类来创建方法而不是使用构造函数,并且在构造函数外部(但在类内部)创建此方法,则将是相同的对于每个对象实例,就像使用原型时一样。
以下是创建fullName()
方法的示例:
class Person {
constructor () {
var self = this;
self.firstName = null;
self.lastName = null;
}
fullName () {
return self.firstName + self.lastName;
}
}
Person.prototype.fullName2 = function () {
return this.firstName + this.lastName;
};
var a = new Person();
var b = new Person();
console.log(a.fullName == b.fullName); // returns true
console.log(a.fullName2 == b.fullName2); // returns true
我希望这会有所帮助。