简体   繁体   English

构造函数与原型内部的函数

[英]Functions inside constructor vs prototype

I know there are similar questions like this, but I want to see if those answers are still valid given optimizations in new Javascript engines. 我知道也有类似的问题,但是我想看看在新的Javascript引擎中进行优化后,这些答案是否仍然有效。

In my opinion, the biggest benefit about defining functions inside the constructor is that you can easily avoid having to know the value of 'this' keyword: 我认为,在构造函数内部定义函数的最大好处是,您可以轻松地不必知道'this'关键字的值:

var Person = function() {
  var self = this;
  self.firstName = null;
  self.lastName = null;
  self.fullName = function() {
    return self.firstName + self.lastName;
  };
};

This approach is recommended by Knockout Managing 'this' . 淘汰管理“ this”建议使用此方法。 This is a great advantage, especially when the code is being modified by many developers as it is really simple to understand and use. 这是一个很大的优势,特别是当许多开发人员正在修改代码时,因为它很容易理解和使用。

The other approach would be to use the object prototype: 另一种方法是使用对象原型:

var Person = function() {
  this.firstName = null;
  this.lastName = null;
};
Person.prototype.fullName = function() {
  return this.firstName + this.lastName;
};

In this case there are performance advantages because functions objects will be created once. 在这种情况下,由于功能对象将被创建一次,因此具有性能优势。 However the main issue I have with it is that it might be complicated to handle the 'this' keyword. 但是我遇到的主要问题是处理'this'关键字可能很复杂。 The above example is very simple, but if you have event handlers, forEach calls, jQuery each() calls, methods being call from different contexts, etc., it is easy to make a bad use of this. 上面的例子非常简单,但是如果您有事件处理程序,forEach调用,jQuery each()调用,从不同上下文中调用方法等,那么很容易就不好用了。

Of course, if you understand how 'this' works and are aware of how methods are being called, you shouldn't have much issues. 当然,如果您了解“这”是如何工作的并且知道如何调用方法,那么您应该不会有太多问题。 However, in my experience, this takes time and it is error prone, especially when code is crafted by many developers. 但是,以我的经验,这需要时间,而且容易出错,尤其是当许多开发人员编写代码时。

I know that new JS engines, like V8, are applying optimizations to cases where you declare functions inside the constructor by creating hidden classes: How the V8 engine works? 我知道像V8这样的新JS引擎正在通过创建隐藏类对在构造函数中声明函数的情况进行优化: V8引擎如何工作? .

So my question is, given these optimizations done by new JS engines and the complexity of having to handle the 'this' keyword, does it still make sense to use the prototype based approach? 所以我的问题是,考虑到新JS引擎完成的这些优化以及必须处理'this'关键字的复杂性,使用基于原型的方法是否仍然有意义? What I would loose by using the approach of putting everything inside the constructor? 通过使用将所有内容放入构造函数的方法,我会失去什么?

UPDATE 1: 更新1:

I just did a micro-benchmark on Chrome (version 42). 我只是在Chrome(版本42)上做了一个微基准测试。 I create 1M objects with functions inside constructor and functions in prototype. 我用构造函数内部的函数和原型中的函数创建1M对象。 It is a very simple object with two variables and three functions and the results are like this: 这是一个非常简单的对象,具有两个变量和三个函数,结果如下所示:

Functions inside constructor: 1.91 seconds
Functions in prototype: 1.10 seconds

Sounds like even with those optimizations in V8 it is still 73% faster. 听起来,即使在V8中进行了这些优化,它仍然快了73%。 However this was a micro-benchmark. 但这是一个微观基准。 Not sure if that will be a big difference in real world applications. 不确定在现实应用中这是否会有很大的不同。

UPDATE 2: 更新2:

I also took a look at memory consumption and there are big differences as well. 我还研究了内存消耗,并且也存在很大差异。 For functions inside constructors: 对于构造函数内部的函数:

Shallow size: 64,000,120
Retained size: 336,001,128

For prototype functions: 对于原型功能:

Shallow size: 40,000,000
Retained size: 40,000,000

Either optimizations with hidden class are not that good or I'm missing something about that. 使用隐藏类进行的优化不是很好,否则我会丢失一些东西。 I'm using monomorphic code (constructors with no args) as suggested by V8, but not sure if I'm doing something wrong. 我使用的是V8建议的单态代码(不带args的构造函数),但不确定我做错了什么。

UPDATE 3: 更新3:

Here is the link of the test I did in case someone can point out something wrong in there: http://jsperf.com/dg-constructor-vs-prototype 这是我所做的测试的链接,以防有人指出那里的错误: http : //jsperf.com/dg-constructor-vs-prototype

I perform a quick test. 我执行快速测试。 If you declare function in the constructor, two object instances have different function instances even after optimizations. 如果在构造函数中声明函数,则即使经过优化,两个对象实例也具有不同的函数实例。 However with prototype, you have only one instance of the function which explains the performance difference. 但是,使用原型时,只有一个功能实例可以解释性能差异。

  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 

I have been using a slightly different approach which IMO has the advantage of clarity and also avoids creating the member functions afresh every time the constructor is called: 我一直在使用一种略有不同的方法,IMO具有清晰性的优点,并且避免了每次调用构造函数时都重新创建成员函数:

  • define member functions of classes as module-level functions outside of the constructor 在构造函数之外将类的成员函数定义为模块级函数
  • explicitly assign the functions to the class instance within the constructor 在构造函数中将函数显式分配给类实例

For example: 例如:

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

There will only be a single instance of the member function which is shared by all the instances of the same class - as is the case when assigning a function to class.prototype.function - so this approach is efficient, and has these additional advantages, IMO: 成员函数只有一个实例,该成员函数由同一类的所有实例共享(就像将函数分配给class.prototype.function时的情况一样),因此该方法非常有效,并且具有其他优点,海事组织:

  1. The constructor explicitly includes declarations of all of the methods of the class, instead of having methods added to the prototype outside of the constructor. 构造函数显式包括该类所有方法的声明,而不是在构造函数之外将方法添加到原型中。
  2. The code of the constructor less cluttered and easier to follow than it is if the whole function definition is coded within the constructor. 与将整个函数定义都编码在构造函数中相比,构造函数的代码更简洁,更易于遵循。
  3. As the method is defined at module level, it is instantiated before the module is executed, so the class can be referred to in code that precedes the constructor declaration. 由于该方法是在模块级别定义的,因此在执行模块之前将其实例化,因此可以在构造函数声明之前的代码中引用该类。 This is not the case when assigning to class.prototype.function, which must be executed before the constructor can be called. 分配给class.prototype.function时不是这种情况,必须在调用构造函数之前执行该类。

Usage: 用法:

// 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...

Are there any downsides to this approach? 这种方法有什么缺点吗?

Like @Ersin Basaran mentioned, a function created inside the constructor is unique for every object instance, unlike when it is created using the prototype makes it the same function for every object instance. 就像@Ersin Basaran提到的那样,在构造函数内部创建的函数对于每个对象实例都是唯一的,与使用原型创建该函数时,它对于每个对象实例都具有相同的功能不同。

However, after introducing classes in ES6 (ECMAScript2015), if you use a class to create a method instead of using a constructor function, and you create this method outside the constructor (but inside the class), it will be the same for every object instance, just like when using the prototype. 但是,在ES6(ECMAScript2015)中引入类之后,如果使用类来创建方法而不是使用构造函数,并且在构造函数外部(但在类内部)创建此方法,则每个对象都相同实例,就像使用原型时一样。

Here is an example of creating a fullName() method: 这是创建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

I hope this helps. 我希望这有帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM