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