new
,this 就會被綁到全域變數上。建立一個原型及擴展其 prototype
var Mammal = function (name) {
this.name = name;
}
Mammal.prototype.get_name = function ( ){
return this.name;
}
Mammal.prototype.says = function ( ){
return this.saying || '';
}
建立實例:
var myMammal = new Mammal('Herb the Mammal');
var name = myMammal.get_name( ); // Herb the Mammal
建立一個繼承 Mammal 的擬類別:
var Cat = function (name) {
this.name = name;
this.saying = 'meow';
}
Cat.prototype = new Mammal( );
Cat.prototype.getName = function (){
return this.says( ) + '' + this.name + ' ' + this.says( );
};
var myCat = new Cat('Henrietta');
var says = myCat.says( );//'meow'
var name = myCat.get_name( );
// 'meow Henrietta meow'
這樣繼承原型步驟繁瑣。
定義一個繼承方法,可以隱藏醜陋的地方:
Function.method('inherites', function (Parent) {
this.prototype = new Parent( );
return this;
})
使用 inherites 方法與 method 方法均回傳 this,再利用 cascade 風格,打造簡潔的程式碼:
var Cat = function (name){
this.name = name;
this.saying = 'meow';
}.inherites(Mammal).
method('get_name', function( ) {
return this.says( ) + '' + this.name + ' ' + this.says( );
});
有時候會在建構式增加許多參數,有時候引數順序不太會被記得:
var myObject = maker(f, l, m, c, s);
我們可以寫一個接受單一物件的接收器:
var myObject = maker({
first: f,
last: l,
state: s,
city: c
});
將物件繼承到另外一個物件:
var Mammal = function (name) {
this.name = name;
}
Mammal.prototype.get_name = function ( ){
return this.name;
};
Mammal.prototype.says = function ( ){
return this.saying || '';
};
var myCat = Object.beget(myMammal);
myCat.name = 'Henrietta';
myCat.saying = 'meow';
myCat.get_name = function ( ) {
return this.says + ' ' + this.name + ' ' + this.says;
}
像是這樣物件繼承了另一個物件,但新物件又被賦予與原型相同的特性,這樣叫做差別繼承。
目前看到的繼承模式都有一個缺點,就是沒有私有成員。
var Mammal = function (spec) {
var that = {};
that.get_name = function ( ){
return spec.name;
}
that.says = function ( ){
return spec.saying || '';
}
return that;
}
var myMammal = mammal({name: 'Herb'});
在由建構式 Mammal 組成實例時,我們傳進 spec 物件,讓建構式中只有特定方法(get_name 和 says)才能取得私有變數(saying 和 name)。
之前在擬類別模式中,實例建立之後,還需要重複建構式的工作(差異繼承),但藉由函式繼承模式就不必這樣,僅需要呼叫建構式,讓建構式負責設定的工作。
var cat = function (spec) {
spec.saying = spec.saying || 'meow';
var that = mammal(spec);
that.get_name = function ( ) {
return that.says( ) + ' ' + spec.name + ' ' + that.says( );
}
return that;
}
這個模式也有處理 super 方法的方式,所以我們來製作一個 superior 方法,即使特性更改了,也會呼叫父層的方法:
Object.method('superior', function (name) {
var that = this;
method = that[name];
return function ( ) {
return method .apply(that, arguments);
};
});
接下來我們來命名一個 super_get_name 方法,把呼叫 superior 的結果指派給他:
var coolcat = function (spec) {
var that = cat(spec);
super_get_name = that.superior('get_name');
that.get_name = function (n){
return 'like' + super_get_name( ) + 'baby';
};
};
var myCoolCat = coolcat({name: 'Bix'});
var name = myCoolCat.get_name( );
// 'like meow Bix meow Baby'
這個模式做到很好的隱藏資訊及取用 super 方法。
我們現在來做一個簡易事件處理功能的函式:
var enentuality = function (that){
vat registry = {};
that.fire = function (event){
// 對物件發出事件,事件是可以包含事件名稱的字串,或是一個物件,
// 該物件裡包含的 type 特性,保存了事件名稱。
// 處裡器由 on 方法登記,
// 其中符合事件名稱的處裡器將被呼叫
var array,
func,
handler,
i,
type = typeof event === 'string' ? event : event.type;
// 如果事件有個處裡器陣列,則以迴圈方式處裡陣列,並依序執行處裡器。
if (registry.hasOwnPreoperty(type)){
array = registry[type];
for (i = 0; i < array.length; i += 1){
handler = array[i];
// 處裡器的紀錄,包含一個方法與一個選用參數陣列,
// 如果方法是個名稱,則尋找符合該名稱的函式
func = handler.hethod;
if (typeof func === 'string'){
func = this[func];
}
// 呼叫處裡器。如果紀錄包含參數,則傳遞參數,否則傳遞 event 物件。
func.apply(this,handler.paramerers || [event]);
}
}
return this;
};
that.on = function (type,method,paramerers){
// 登記一個事件(event)。製作處裡器紀錄。
// 放入處裡器陣列,如果類型事件尚未存在,則製造一個。
var handler = {
method : method,
paramerers : paramerers
};
if (registry.hasOwnPreoperty(type)){
registry[type].push(handler);
} else {
registry[type] = [handler];
}
return this;
};
return that;
};
eventuality(that);
我們可以在任何物件裡呼叫 eventuality,給它事件處裡的方法。我們也可能回傳 that 前,在建構式裡呼叫 eventuality(that) 。這樣建構式可從一組零件組合出物件。
資料來源:《JavaScript 優良部份》 Douglas Crockford 著 歐萊禮
筆記純屬推廣及分享,如有侵權,請告知。
Please advise to remove immediately if any infringement caused.