從設計模式的角度講,原型模式是用于創建對象的一種模式,如果我們想要創建一個對象,一種方法是先指定它的類型,然后通過類來創建這個對象.原型模式選擇了另外一種方式,我們不在關系對象的具體類型,而是找到一個對象,然后通過克隆創建一個一模一樣的對象.
原型模式的實現關鍵,是語言本身是否提供了clone方法.ECMAScript5提供了Object.create方法,可以用來克隆對象.
var Plane = function(){
this.blood = 100;
this.attackLevel = 1;
this.defenseLevel = 1;
};
var plane = new Plane();
plane.blood = 500;
plane.attackLevel = 10;
plane.defenseLevel = 7;
var clonePlane = Object.create(plane);
console.log(clonePlane);
//在不支持Object.create方法的瀏覽器中,可以使用:
Object.create = Object.create||function(obj){
var F=function(){};
F.prototype = obj;
return new F();
}
原型模式的真正目的并非在于需要得到一個一模一樣的對象,而是提供了一種便捷的方式去創建某個類型的對象,克隆只是創建這個對象的過程和手段。
javascrpit本身是一門基于原型的面向對象的語言,它的對象系統就是使用原型模式來搭建的,在這里稱之為原型編程范型也許更合適。
JavaScript中的原型繼承 原型編程范型至少要包括以下基本規則:
1.所有的數據都是對象
- 所有的數據都是對象
- 要得到一個對象,不是通過實例化類,而是找到一個對象作為原型并克隆它。
3.對象會記住它的原型。
4.如果對象無法響應某個請求,它會把這個請求委托給它自己的原型。
試試上,JavaScript中的根對象是object.prototype對象。object.prototype對象是一個空的對象。我們在JavaScript遇到的每個對象,實際上都是從object.prototype對象克隆而來的,object.prototype對象就是它們的原型。
可以利用ECMAScript5提供的Object.getPrototypeOf來查看這兩個對象的原型:
var obj1 = new Object();//var obj1 = {};
console.log(Object.getPrototypeOf(obj1)===Object.prototype);//輸出:true
2.要得到一個對象,不是通過實例化類,而是找到一個對象作為原型并克隆它
在JavaScript語言里,我們并不需要關心克隆的細節,因為這是引擎內部負責實現的。我們所需要做的只是顯式地調用var obj1 = new object()或者var obj1 = {}。此時,引擎內部會從object.prototype上面克隆一個對象出來,我們最終得到的就是這個對象。
3.對象會記住它的原型
如果請求可以在一條鏈條中依次往后傳遞,那么每個節點都必須知道它的下一個節點。JavaScript語言中的原型鏈查找機制,每個對象至少應該記住它自己的原型。
JavaScrpit的真正來說,其實并不能說對象有原型,而只能說對象的構造器的原型。
JavaScript給對象提供了一個名proto的隱藏屬性,某個對象proto屬性默認會指向它的構造器的原型對象,即{Constructor}.prototype。實際上,proto就是對象跟“對象構造器的原型”聯系起來的紐帶。
4. 如果對象無法響應某個請求,它會把這個請求委托給它的構造器的原型
JavaScript的對象最初都是由Object.prototype對象克隆而來的,但對象構造器的原型并不僅限于Object.prototype上,而是可以動態指向其他對象.這樣一來,當對象a需要借用對象b的能力時,可以有選擇性的把對象a的構造器的原型指向對象b,從而達到繼承的效果.
var obj = {name:'sven'};
var A = function(){};
A.prototype = obj;
var a = new A();
console.log(a.name); //輸出:sven
我們來看看執行這段代碼的時候,引擎做了哪些事情:
- 首先, 嘗試遍歷對象a中的所有屬性,但沒有找到name這個屬性.
- 查找name屬性的這個請求被委托給對象a的構造器的原型,它被a.proto記錄著并且指向A.prototype,而A.prototype被設置為對象obj.
- 在對象obj中找到了name屬性,并返回它的值.
當我們期望得到一個"類"繼承自另外一個"類"的效果時:
- 首先,嘗試遍歷對象b中的所有屬性,但沒有找到name這個屬性.
- 查找name屬性的請求被委托給對象b的構造器的原型,它被b.proto記錄著并指向B.prototype,而B.prototype被設置為一個通過new A()創建出來的對象.
- 該對象中依然沒有找到name屬性,于是請求被繼續委托給這個對象構造器的原型A.prototype.
- 在A.prototype中找到了name屬性,并返回它的值.
在當前的JavaScript引擎下,通過Object.create來創建對象的效率并不高,通常比通過構造函數創建對象要慢.另外,通過設置構造器的prototype來實現原型繼續的時候,除了根對象Object.prototype本身之外,任何對象都會有一個原型.而通過Object.create(null)可以創建出沒有原型的對象.