Javascript 中的建構函式與其他語言相比也是不同的。任何通過關鍵字 new 調用的函數都可以當做建構函式。
在建構函式體內,this 指向新建立的對象。如果建構函式體內沒有顯示的 return 運算式,那麼我們就預設返回 this,也就是建立的對象。
複製代碼 代碼如下:
function Foo() {
this.bla = 1;
}
Foo.prototype.test = function() {
console.log(this.bla);
};
var test = new Foo();
上面的代碼將 Foo 作為建構函式進行調用,並將建立對象的原型(__proto__)指向了 Foo.prototype。
如果我們在建構函式內定義返回的 return 運算式,建構函式就會返回整個運算式,但這個返回運算式必須為一個對象。
複製代碼 代碼如下:
function Bar() {
return 2;
}
new Bar(); // a new object
function Test() {
this.value = 2;
return {
foo: 1
};
}
new Test(); // the returned object
如果 new 被省略,那麼函數將不能返回一個新的對象。
複製代碼 代碼如下:
function Foo() {
this.bla = 1; // gets set on the global object
}
Foo(); // undefined
上面的例子可能在某些情境下也可以運行,但由於 Javascript 中 this 的工作機制,這裡 this 將指向全域對象。
原廠模式
為了能夠不使用關鍵字 new,建構函式將不得不顯示返回一個值。
複製代碼 代碼如下:
function Bar() {
var value = 1;
return {
method: function() {
return value;
}
}
}
Bar.prototype = {
foo: function() {}
};
new Bar();
Bar();
上例中使不使用 new 來調用函數 Bar 達到的效果是一樣的,將會返回一個建立的包含 method 方法的對象,這裡實際上就是一個閉包。
這裡需要注意一點,new Bar() 將不會返回 Bar.prototype,而是在 return 運算式內函數 method 的原型對象。
上例中,使用 new 與否在功能上是無差異的。
通過原廠模式建立新的對象
我們經常被提醒不要使用 new,因為一旦忘記了它的使用將導致錯誤。
為了建立一個對象,我們更願意使用原廠模式並在原廠模式內構造一個新的對象。
複製代碼 代碼如下:
function Foo() {
var obj = {};
obj.value = 'blub';
var private = 2;
obj.someMethod = function(value) {
this.value = value;
}
obj.getPrivate = function() {
return private;
}
return obj;
}
儘管上例代碼比使用 new 時更不容易出錯,而且在使用私人變數時將更加方便,但同時也有一些不好的地方:
因為不能共用原型對象,所以需要更多的記憶體。
為了實現繼承,原廠模式需要拷貝另一個對象的所有方法或者將其作為新對象的原型。
放棄原型鏈只是為了避免使用 new,這似乎與 Javascript 語言的精神相悖。
總結
儘管使用 new 可能比較容易產生錯誤,但這並不能成為放棄使用原型鏈的原因。至於最後採取哪種方式,這需要根據應用的需求而定。最好的方式就是選擇一種風格並堅持下去。
簡單的說建構函式就是初始化一個執行個體對象,對象的prototype屬性是繼承一個執行個體對象。