Javascript有多種創建對象的方式,新手和老手都可能感到不知所措,不確定應該使用哪種方式。文本將介紹常見的對象常見模式和最佳的實踐。




對象字面量:Object Literals
創建對象最簡單的方式就是對象字面量。Javascript總是吹噓能夠“無中生有”地創建對象——不需要類、不需要模板、不需要原型——just poof!,一個有方法和數據的對象就出現了。
var o = {
x: 42,
y: 3.14,
f: function() {},
g: function() {}
};
缺點
如果需要在其他地方創建相同類型的對象,將要復制粘貼對象的方法、數據和初始化的代碼,導致大量重復代碼。需要一種能夠批量創建同類型對象的方法,而不僅僅是一個對象。
工廠模式:Factory Functions
這是創建相同結構、接口和實現的對象最簡單的方式。而不是直接創建對象字面量,而是將對象字面量作為函數的返回值。這樣,如果需要多次或多個地方創建相同類型的對象時只需要調用一個函數:
function thing() {
return {
x: 42,
y: 3.14,
f: function() {},
g: function() {}
};
}
var o = thing();
缺點
這種Javascript對象創建方法可能會導致內存膨脹,因為每一個對象都包含了工廠函數的獨立副本。理想情況下,我們希望每個對象只共享其功能的一個副本。
構造函數模式
可以創建特定類型的對象, 類似于Array, Date等原生JS的對象.其實現方法如下:
function Student(name,age){
this.name=name;
this.age=age;
this.myName=function(){
alert(this.name);
};
}
var student1_ = new Student('aaa',15);
var student2_ = new Student('bbb',18);
缺點
每次實例化一個對象都會把構造器里的所有方法重新創建一次,多次創建會造成內存開銷增加的問題。
原型鏈:Prototype Chains
Javascript提供了一種內置的在對象之間共享數據的機制,稱為原型鏈。當訪問對象的屬性時,它可以通過委托給其他對象來滿足該請求??梢岳眠@一點來修改工廠函數,使它創建的每個對象只包含自己特有的數據,而對其他屬性的請求則全部委托給原型鏈上共有的一個對象:
var thingPrototype = {
f: function() {},
g: function() {}
};
function thing() {
var o = Object.create(thingPrototype);
o.x = 42;
o.y = 3.14;
return o;
}
var o = thing();
事實上,這是一種常見的模式,語言已經內置了對它的支持。不需要創建自己的共享對象(原型對象)。相反,會自動為每個函數創建一個原型對象,可以將共享數據放在那里:
thing.prototype.f = function() {};
thing.prototype.g = function() {};
function thing() {
var o = Object.create(thing.prototype);
o.x = 42;
o.y = 3.14;
return o;
}
var o = thing();
缺點
會導致重復。上述thing函數的第一行和最后一行在每一個“委托原型的工廠函數”中都會重復一次,幾乎沒有區別。
ES5類:ES5 Classes
可以把那些重復的代碼抽出來,放進一個自定義函數里來隔離它們。這個函數會創建一個對象,并與其他某個任意函數(參數函數)的原型建立委托(繼承)關系,然后把新創建的對象作為參數,調用這個函數(參數函數),最后返回這個新的對象。
function create(fn) {
var o = Object.create(fn.prototype);
fn.call(o);
return o;
}
// ...
Thing.prototype.f = function() {};
Thing.prototype.g = function() {};
function Thing() {
this.x = 42;
this.y = 3.14;
}
var o = create(Thing);
事實上,這也是一種常見的模式,Javascript有一些內置的支持。create定義的函數實際上是new關鍵字的基本版本,可以直接替換create為new(構造函數+原型鏈):
Thing.prototype.f = function() {};
Thing.prototype.g = function() {};
function Thing() {
this.x = 42;
this.y = 3.14;
}
var o = new Thing();
在ES5中,它們是對象創建函數,它將共享數據委托給原型對象,并依賴new關鍵字來處理重復邏輯。
缺點
冗長和丑陋,實現繼承更加冗長和丑陋。
ES6類:ES6 Classes
在ES6的類中,執行相同的操作提供了更清晰的語法:
class Thing {
constructor() {
this.x = 42;
this.y = 3.14;
}
f() {}
g() {}
}
const o = new Thing();
比較
多年以來,Javascript開發者們與原型鏈的關系總是若即若離,糾纏不清。而今天最有可能遇到的兩種創建對象的方式,一種是強烈依賴原型鏈的class語法,另一種則是完全不依賴原型鏈的工廠函數語法。這兩種風格在性能和功能上有所不同——盡管差別不太大。
性能
今天Javascript引擎已經過如此大量優化,以至于很難通過Javascript代碼來推斷怎樣會比較快。關鍵在于測量方法。然而有時甚至測量都會讓我們失望。通常情況下,每六周發布一次更新的Javascript引擎,有時性能會發生重大變化,我們之前進行的任何測量以及我們根據這些測量做出的任何決定都會立即出現。因此,經驗法則是支持最官方和最廣泛使用的語法,假設它將受到最嚴格的審查,并且在大多數時候是最高效的。目前來看class語法最符合這一點,class語法大約比返回字面量的工廠模式快3倍。
特點
隨著ES6的發布,類與工廠模式之間曾經存在的幾點差異消失了。現在,工廠模式和類都能夠強制實現真正的私有數據:
- 工廠模式通過閉包實現
- 類通過weak maps實現
兩者都能實現多重繼承——工廠模式可以將其他屬性混入自己的對象,類也可以將其他屬性混入自己的原型,或者通過類工廠,通過代理也能實現。工廠函數和類也都可以在需要的時候返回任意對象,語法也都很簡單。
結論
考慮到所有事情,對Javascript對象創建的偏好是使用類語法。它是標準的,它簡單而干凈,速度快,并且它提供了曾經只有工廠模式才能提供的所有功能
版權聲明:本文內容由互聯網用戶自發貢獻,該文觀點僅代表作者本人。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如發現本站有涉嫌抄襲侵權/違法違規的內容, 請發送郵件至 舉報,一經查實,本站將立刻刪除。