簡體   English   中英

構造函數與原型內部的函數

[英]Functions inside constructor vs prototype

我知道也有類似的問題,但是我想看看在新的Javascript引擎中進行優化后,這些答案是否仍然有效。

我認為,在構造函數內部定義函數的最大好處是,您可以輕松地不必知道'this'關鍵字的值:

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

淘汰管理“ this”建議使用此方法。 這是一個很大的優勢,特別是當許多開發人員正在修改代碼時,因為它很容易理解和使用。

另一種方法是使用對象原型:

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

在這種情況下,由於功能對象將被創建一次,因此具有性能優勢。 但是我遇到的主要問題是處理'this'關鍵字可能很復雜。 上面的例子非常簡單,但是如果您有事件處理程序,forEach調用,jQuery each()調用,從不同上下文中調用方法等,那么很容易就不好用了。

當然,如果您了解“這”是如何工作的並且知道如何調用方法,那么您應該不會有太多問題。 但是,以我的經驗,這需要時間,而且容易出錯,尤其是當許多開發人員編寫代碼時。

我知道像V8這樣的新JS引擎正在通過創建隱藏類對在構造函數中聲明函數的情況進行優化: V8引擎如何工作?

所以我的問題是,考慮到新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

我執行快速測試。 如果在構造函數中聲明函數,則即使經過優化,兩個對象實例也具有不同的函數實例。 但是,使用原型時,只有一個功能實例可以解釋性能差異。

  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 

我一直在使用一種略有不同的方法,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時的情況一樣),因此該方法非常有效,並且具有其他優點,海事組織:

  1. 構造函數顯式包括該類所有方法的聲明,而不是在構造函數之外將方法添加到原型中。
  2. 與將整個函數定義都編碼在構造函數中相比,構造函數的代碼更簡潔,更易於遵循。
  3. 由於該方法是在模塊級別定義的,因此在執行模塊之前將其實例化,因此可以在構造函數聲明之前的代碼中引用該類。 分配給class.prototype.function時不是這種情況,必須在調用構造函數之前執行該類。

用法:

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

這種方法有什么缺點嗎?

就像@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

我希望這有幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM