JavaScript實現建立自定義物件的常用方式總結
- 物件字面量方式
物件字面量方式是建立自定義物件的首選模式,簡單方便。
var per = {
name:'zhangsan',
age:25,
job:'html',
sayName:function(){
alert(this.name);
}
}
**缺點:**使用同一個介面建立很多物件,會產生大量的重複程式碼。比如我想再建立一個per1物件,我就得把上面的程式碼再重新寫一遍,改變不同的屬性值。
2、工廠模式
工廠模式抽象了建立具體物件的過程。由於在ECMAScript中無法建立類,開發人員就發明了一種函式,用函式來封裝以特定介面建立物件的細節,如下面的例子:
function createPerson(name,age,job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); } return o; } var person1 = createPerson('zhang',30,'java'); var person2 = createPerson('zhao',25,'php'); //前端全棧學習交流圈:866109386 //面向1-3經驗年前端開發人員 //幫助突破技術瓶頸,提升思維能力
函式createPerson()能夠根據接受到的引數來構建一個包含所有必要資訊的Person物件。可以無數次的呼叫這個函式,而每次它都會返回一個包含三個屬性和一個方法的物件。
缺點:工廠模式雖然解決了建立多個相似物件的問題,但卻沒有解決物件識別的問題(即怎樣知道一個物件的型別)。
3、建構函式模式
可以使用建構函式模式將前面的例子重寫如下:
function Person(name,age,job){ this.name= name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); } } var person1 = new Person('zhang',30,'java'); var person2 = new Person('zhao',25,'php'); //前端全棧學習交流圈:866109386 //面向1-3經驗年前端開發人員 //幫助突破技術瓶頸,提升思維能力
建立自定義的建構函式意味著將來可以將它的例項標識為一種特定的型別。而這正是建構函式模式勝過工廠模式的地方。
然而,使用建構函式的主要問題,就是每個方法都要在每個例項上重新建立一遍。在上面的例子中,person1和person2都有一個名為sayName()的方法,但那兩個方法不是同一個Function的例項,建立兩個完成同樣任務的Function例項的確沒有必要;況且有this物件在,根本不用在執行程式碼前就把函式繫結到特定物件上面。因此可以像下面這樣,通過把函式定義轉移到建構函式外部來解決這個問題。
function Person(name,age,job){
this.name= name;
this.age = age;
this.job = job;
this.sayName = sayName();
}
function sayName(){
alert(this.name);
}
var person1 = createPerson('zhang',30,'java');
var person2 = createPerson('zhao',25,'php');
在這個例子中,我們把sayName()函式的定義轉移到建構函式外部。而在建構函式內部,我們將sayName屬性設定成等於全域性的sayName函式,這樣person1和person2物件就共享了在全域性作用域中定義的同一個sayName()函式。這樣確實解決了兩個函式在做同一件事的問題,可是新問題又來了:在全域性作用域中定義的函式實際上只能被某個物件呼叫,這讓全域性作用域有點名不副實。更讓人無法接受的是:如果物件需要定義很多方法,那麼就要定義很多全域性函式。好在,這些問題可以通過使用原型模式來解決。
4、原型模式
我們建立的每個函式都有一個prototype(原型)屬性,這個屬性是一個指標,指向一個物件,而這個物件的用途是包含可以由特定型別的所有例項共享的屬性和方法。使用原型物件的好處是可以讓所有的物件例項共享他所包含的屬性和方法。
function Person(){}
Person.prototype.name = 'zhang';
Person.prototype.age = '22';
Person.prototype.job = 'html5';
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
var person2 = new Person();
原型模式也不是沒有缺點。首先,它省略了為建構函式傳遞初始化引數這一環節,結果所有例項在預設情況下都將取得相同的屬性值。雖然這會在某種程度上帶來一些不方便,但還不是原型的最大問題。原型模式的最大問題是由共享的本性所導致的。
原型中所有屬性是被很多例項共享的,這種共享對於函式非常合適。對於那些包含基本值的屬性倒也說的過去,通過在例項上新增一個同名屬性,可以隱藏原型中的對應屬性。然後,對於包含引用型別的屬性來說,問題就比較突出了。
function Person(){}
Person.prototype = {
constructor:Person,
name:'zhang',
age :'22',
job :'html5',
friends:['wang','li'],
sayName : function(){
alert(this.name);
}
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push('zhao');
alert(person1.friends); //'wang,li,zhao'
alert(person2.friends); //'wang,li,zhao'
alert(person1.friends === person2.friends); //true
從上面的列印的結果我們就可以知道為什麼很少人單獨使用原型模式了,例項一般都是要有屬於自己的全部屬性的。
5、組合使用建構函式模式和原型模式
組合使用建構函式模式和原型模式,是建立自定義型別的最常見方式。建構函式模式用於定義例項屬性,而原型模式用於定義方法和共享的屬性。結果,每個例項都會有自己的一份例項屬性的副本,但同時又共享著對方法的引用,最大限度的節省了記憶體。
function Person(name,age,job){
this.name= name;
this.age = age;
this.job = job;
this.friends = ['wang','li'];
}
Person.prototype = {
constructor:Person,
sayName : function(){
alert(this.name);
}
}
var person1 = new Person('zhang',26,'java',);
var person2 = new Person('sun',25,'php');
person1.friends.push('zhao');
alert(person1.friends); //'wang,li,zhao'
alert(person2.friends); //'wang,li'
alert(person1.friends === person2.friends); //false
在上面的例子中,例項屬性都是在建構函式中定義的,而由所有例項共享的屬性constructor和方法satName()則是在原型中定義的。而修改了person1.friends(向其中新增一個新字串),並不會影響到person2.friends,因為他們分別引用了不同的陣列。