許多 OO 語言支持兩種繼承方式:接口繼承和實現繼承。接口繼承只繼承方法簽名,而實現繼承則繼承實際的方法。如前所述,因爲函數沒有簽名,在 ECMAScript 中沒法實現接口繼承。 ECMAScript 只支持實現繼承,並且其實現繼承主要是依靠原型鏈來實現。app
基本思想是:利用原型讓一個引用類型繼承另外一個引用類型的屬性和方法。
構造函數、原型和實例的關係:每一個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的內部指針。函數
jsfunction SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function() { return this.property; }; function SubType() { this.subproperty = false; } //繼承了 SuperType SubType.prototype = new SuperType(); SuperType.prototype.getSubValue = function() { return this.subproperty; } var instance = new SubType(); alert(instance.getSuperValue());//true
別忘記默認的原型this
肯定原型和實例的關係spa
jsalert(instance instanceof Object); //true alert(instance instanceof SuperType); //true alert(instance instanceof SubType); //true
jsalert(Object.prototype.isPrototypeOf(instance));//ture alert(SuperType.prototype.isPrototypeOf(instance));//ture alert(SubType.prototype.isPrototypeOf(instance));//ture
謹慎地定義方法prototype
給原型添加方法的代碼必定要放在替換原型的語句以後。指針
jsfunction SuperType() { this.property = true; } SuperType.protype.getSuperValue = function() { return this.property; } function SubType() { this.subproperty = false; } //繼承了 SuperType SubType.prototype = new SuperType(); //添加新方法 SubType.prototype.getSubValue = function() { return this.subproperty; }; //重寫超類型中的方法 SubType.prototype.getSuperValue = function() { return false; }; var instance = new SubType(); alert(instance.getSuperValue());//false
jsfunction SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function() { return this.property; } function SubType() { this.subproperty = false; } //繼承了 SuperType SubType.prototype = new SuperType(); //使用字變量添加新方法,會致使上一行代碼無效 SubType.prototype = { getSubValue : function() { return this.subproperty; }, someOtherMethod = function() { return false; } }; var instance = new SubType(); alert(instance.getSuperValue()); //error 原型鏈被切斷(如今的原型包含的是 Object 的實例)。
4.原型鏈的問題code
最主要的問題來自包含引用類型值的原型。前面已經介紹過包含引用類型值的原型屬性會被全部實例共享,這也是爲何要在構造函數中而不在原型對象中定義屬性的緣由。
第二個問題是:在建立子類型的實例時,不能向超類型的構造函數中傳遞參數。實際上,應該說是沒有辦法在不影響全部對象實例的狀況下,給炒類型的構造函數傳遞參數。對象
即在子類型構造函數的內部調用超類型構造函數。函數只不過是在特定環境中執行代碼的對象,因此可經過 apply()
或 call()
方法也能夠在(未來)新建立的對象上執行構造函數。blog
jsfunction SuperType() { this.colors = ["red","blue","green"]; } function SubType() { //繼承了 SuperType SuperType.call(this); } var instance1 = new SubType(); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" var instance2 = new SubType(); alert(instance2.colors); //"red,blue,green"
傳遞參數繼承
jsfunction SuperType(name) { this.name = name; } function SubType() { //繼承了 SuperType,同時還傳遞了參數 SuperType.call(this,"PaddingMe"); //實例屬性 this.age = 25; } var instance = new SubType(); alert(instance.name); //"PaddingMe" alert(instance.age); //25
即將原型鏈和借用構造函數的方法組合在一塊兒,思路爲使用原型鏈實現對原型屬性和方法的繼承,而經過借用構造函數來實現對實例屬性的繼承。
jsfunction SuperType(name) { this.name = name; this.colors = ["red","blue","green"]; } SuperType.prototype.sayName = function() { alert(this.name); } function SubType() { //繼承屬性 SuperType.call(this,name); this.age = age; } SubType.prototype = new SuperType(); SubType.prototype.constuctor = SubType(); SubType.prototype.sayAge = function() { alert(this.age); } var instance1 = new("PaddingMe",25); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" instance1.sayName; //"PaddingMe" instance1.sayAge; //25 var instance2 = new("hw",26); alert(instance2.colors); //"red,blue,green" instance2.sayName; //"hw" instance2.sayAge; //26
jsfunction object(o) { function F(){} F.prototype = o; return new F(); } var person = { name : "PaddingMe"; friends :["hw","wjj","hz"]; } var antherPerson = object(person); antherPerson.name = "Hhb"; antherPerson.friends.push("zxp"); var yetAntherPerson = object(person); yetAntherPerson.name = "Linda"; yetAntherPerson.friends.push("him"); alert(person.friends)//"hw,wjj,hz,zxp,him"
ECMAScirpt 5 中新增 Object.create() 方法規範化了原型式繼承。有兩個參數,一個用做新對象原型的對象和(可選的)一個爲新對象定義額外屬性的對象。
在傳入一個參數狀況下,Objetc.create() 和 object() 方法的行爲相同。
jsvar person = { name : "PaddingMe"; friends :["hw","wjj","hz"]; } var antherPerson = Object.create(person); antherPerson.name = "Hhb"; antherPerson.friends.push("zxp"); var yetAntherPerson = Object.create(person); yetAntherPerson.name = "Linda"; yetAntherPerson.friends.push("him"); alert(person.friends)//"hw,wjj,hz,zxp,him"
第二個參數與 `Object.defineProperties() 方法的第二個參數格式相同: 每一個屬性都是經過本身的描述符定義的。
jsvar person = { name : "PaddingMe"; friends :["hw","wjj","hz"]; } var anthorPerson = Object.create(person, { name: { value:"hehe"; } }) alert(anthorPerson.name);//"hehe"
即建立一個僅用於封裝繼承構成的函數,該函數在內部以某種方式來加強對象,最後再像真地是它作了全部工做同樣返回對象。
jsfunction createAnother(original) { var clone = object(original); //經過調用函數建立一個新對象 clone.sayHi = function() { //以某種方式來加強這個對象 alert("hi"); }; return clone; //返回這個對象 } var person = { name : "PaddingMe"; friends :["hw","wjj","hz"]; } var anthorPerson = createAnother(person); anthorPerson.sayHi();//"hi"
jsfunction SuperType(name) { this.name = name; this.colors = ["red","blue","green"]; } SuperType.prototype.sayName = function() { alert(this.name); } function SubType(name,age) { SuperType.call(this,name); //第二次調用SuperType(); this.age = age; } SubType.prototype = new SuperType(); // 第一次調用SuperType(); SubType.prototype.constuctor = SubType; SubType.prototype.sayAge = function(){ alert(this.age); }
所謂寄生組合式繼承,即經過借用構造函數來繼承屬性,經過原型鏈的混成形式來繼承方法。基本思路爲:沒必要爲了指定子類型的原型而調用超類型的構造函數,咱們所須要的無非是超類型原型的一個副本而已。本質上,就是使用寄生式繼承來繼承超類型的原型,而後再將結構指定給子類型的原型。寄生組合式繼承的基本模式:
jsfunction inheritPrototype(subType,superType){ var prototype = object(superType.prototype); //建立對象 prototype.constructor = subType; //加強對象 subType.prototype = prototype; // 指定對象 }
jsfunction SuperType(name) { this.name = name; this.colors = ["red","blue","green"]; } SuperType.prototype.sayName = function() { alert(this.name); } function SubType(name,age) { SuperType.call(this,name); //第二次調用SuperType(); this.age = age; } inheritPrototype(SubType,SuperType); SubType.prototype.sayAge = function(){ alert(this.age); }