JavaScript 中的構造函數(shù)和其它語言中的構造函數(shù)是不同的。
通過 new
關鍵字方式調用的函數(shù)都被認為是構造函數(shù)。
在構造函數(shù)內部 - 也就是被調用的函數(shù)內 - this
指向新創(chuàng)建的對象 Object
。
這個新創(chuàng)建的對象的 prototype
被指向到構造函數(shù)的 prototype
。
如果被調用的函數(shù)沒有顯式的 return
表達式,則隱式的會返回 this
對象 - 也就是新創(chuàng)建的對象。
function Foo() {
this.bla = 1;
}
Foo.prototype.test = function() {
console.log(this.bla);
};
var test = new Foo();
上面代碼把 Foo
作為構造函數(shù)調用,并設置新創(chuàng)建對象的 prototype
為 Foo.prototype
。
顯式的 return
表達式將會影響返回結果,但僅限于返回的是一個對象。
function Bar() {
return 2;
}
new Bar(); // 返回新創(chuàng)建的對象
function Test() {
this.value = 2;
return {
foo: 1
};
}
new Test(); // 返回的對象
譯者注:new Bar()
返回的是新創(chuàng)建的對象,而不是數(shù)字的字面值 2。
因此 new Bar().constructor === Bar
,但是如果返回的是數(shù)字對象,結果就不同了,如下所示
function Bar() {
return new Number(2);
}
new Bar().constructor === Number
譯者注:這里得到的 new Test()
是函數(shù)返回的對象,而不是通過new
關鍵字新創(chuàng)建的對象,因此:
(new Test()).value === undefined
(new Test()).foo === 1
如果 new
被遺漏了,則函數(shù)不會返回新創(chuàng)建的對象。
function Foo() {
this.bla = 1; // 獲取設置全局參數(shù)
}
Foo(); // undefined
雖然上例在有些情況下也能正常運行,但是由于 JavaScript 中this
的工作原理,
這里的 this
指向全局對象。
為了不使用 new
關鍵字,構造函數(shù)必須顯式的返回一個值。
function Bar() {
var value = 1;
return {
method: function() {
return value;
}
}
}
Bar.prototype = {
foo: function() {}
};
new Bar();
Bar();
上面兩種對 Bar
函數(shù)的調用返回的值完全相同,一個新創(chuàng)建的擁有 method
屬性的對象被返回,
其實這里創(chuàng)建了一個閉包。
還需要注意, new Bar()
并不會改變返回對象的原型(譯者注:也就是返回對象的原型不會指向 Bar.prototype
)。
因為構造函數(shù)的原型會被指向到剛剛創(chuàng)建的新對象,而這里的 Bar
沒有把這個新對象返回(譯者注:而是返回了一個包含 method
屬性的自定義對象)。
在上面的例子中,使用或者不使用 new
關鍵字沒有功能性的區(qū)別。
譯者注:上面兩種方式創(chuàng)建的對象不能訪問 Bar
原型鏈上的屬性,如下所示:
var bar1 = new Bar();
typeof(bar1.method); // "function"
typeof(bar1.foo); // "undefined"
var bar2 = Bar();
typeof(bar2.method); // "function"
typeof(bar2.foo); // "undefined"
我們常聽到的一條忠告是不要使用 new
關鍵字來調用函數(shù),因為如果忘記使用它就會導致錯誤。
為了創(chuàng)建新對象,我們可以創(chuàng)建一個工廠方法,并且在方法內構造一個新對象。
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
帶來的問題,這似乎和語言本身的思想相違背。雖然遺漏 new
關鍵字可能會導致問題,但這并不是放棄使用原型鏈的借口。
最終使用哪種方式取決于應用程序的需求,選擇一種代碼書寫風格并堅持下去才是最重要的。