如何優化JavaScript的構造函數

  首先看一個構造函數User,咱們在調用User建立一個實例的的時候,通常都是要寫上new操做符的。 在這裏說明一下,若是使用new關鍵字調用構造函數,那麼構造函數裏面的this老是是指向一個全新的對象(即User的實例),若是不是使用new的話,那麼this就指向global對象。User構造函數的定義以下:
function User(name, passwordHash) {
    this.name = name;
    this.passwordHash = passwordHash;
}

 

 User構造函數的正確的調用方法應該以下:
var u = new User("baravelli", "d8b74df393528d51cd19980ae0aa028e");
 
可是,假如調用者由於粗心,忘記了加上new關鍵字來調用,那結果會怎樣呢?咱們一塊兒來測試一下:
var u = User("baravelli", "d8b74df393528d51cd19980ae0aa028e");
u; // undefined
this.name; // "baravelli"
this.passwordHash; // "d8b74df393528d51cd19980ae0aa028e"
 
結果構造函數居然返回了undefined!!這也很正常的,由於不用new關鍵字,直接調用他的話,那就跟調用普通函數沒有任何區別了,而User裏面又沒有return語句,因此返回值固然是undefined了。更加糟糕的是,若是不加new調用,User的裏面的this就會指向全局對象了,那麼它就會破壞全局對象,試想一下,假如全局對象自己就存在了name和passwordHash這兩個變量,那麼他們的值就會被修改了,這個危害是很大的。
 
若是,構造函數User裏面開啓了ES5的嚴格模式,那麼不使用new操做符就會由於this綁定失敗而拋出錯誤(注意:嚴格模式下是不容許this指向全局對象的),以下:
function User(name, passwordHash) {
    "use strict";
    this.name = name;
    this.passwordHash = passwordHash;
}
 
var u = User("baravelli", "d8b74df393528d51cd19980ae0aa028e");
// error: this is undefined

 

這樣的話,會拋出一個類型錯誤 // error: this is undefined。可是,這個構造函數依然是很脆弱的,由於它只有在加new操做符的時候才能夠正常工做。假如咱們實現了一個構造函數,加不加new關鍵字均可以正常工做那就健壯多了!其實,實現起來也並不太難,咱們只要在User構造函數裏面判斷this是否指向User的實例就好了,若是不是就建立一個User實例,以下:
function User(name, passwordHash) {
    if (!(this instanceof User)) {
        return new User(name, passwordHash);
    }
    this.name = name;
    this.passwordHash = passwordHash;
}
 
如今,無論你用不用new關鍵字來調用構造函數,他均可以正常工做了,測試一下:
var x = User("baravelli", "d8b74df393528d51cd19980ae0aa028e");
var y = new User("baravelli",
"d8b74df393528d51cd19980ae0aa028e");
x instanceof User; // true
y instanceof User; // true
 
  可是上述實現方法還具備一個缺點,由於它兩次調用了User構造函數(在不使用new關鍵字的時候),因此,下降了性能!並且,還有一個問題,就是對於可變參數的構造函數,它實現起來就會很困難的了 。一種較優的辦法就是藉助於ES5的Object.create方法:
function User(name, passwordHash) {
    var self = this instanceof User ? this : Object.create(User.prototype);
    self.name = name;
    self.passwordHash = passwordHash;
    return self;
}

 

 這種方法,藉助了Object.create方法,把User.prototype做爲參數,建立了一個繼承了User的新對象。 可是,這種方法也是有缺陷的,咱們前面已經說過 了,Object.create是ES5的新標準,在一些舊的環境下可能沒法工做。因此,咱們還要判斷Object.create是否存在。,若是不存在,則手動的去實現它:
if (typeof Object.create === "undefined") {
    Object.create = function(prototype) {
        function C() { }
        C.prototype = prototype;
        return new C();
    };
}

 

最後,須要提醒的是,若是你的構造函數必定要使用new關鍵字的,那麼必需要寫文檔說明,以避免別人調用的時候沒有用new操做符,產生意想不到的結果!
相關文章
相關標籤/搜索