在瞭解了js 中的原型鏈以後 (https://segmentfault.com/a/11...),咱們再來看看js 中的幾種實現繼承的方式segmentfault
爲了解決包含引用類型值的原型屬性會被全部實例共享的問題,大神們發明了在子類型構造函數的內部調用超類型構造函數而後經過apply()和call()方法在(未來)新建立的對象上執行構造函數的方式來實現繼承,以下app
function SuperType() { this.colors = ["red", "blue", "green"]; } function SubType() { //調用SuperType 而且經過call()方法修正this指向 SuperType.call(this); } var instance1 = new SubType(); instance1.colors.push("black"); //"red,blue,green,black" alert(instance1.colors); //"red,blue,green" var instance2 = new SubType(); alert(instance2.colors);
以上例子中在SubType()調用了SuperType()構造函數。經過使用call()方法(或apply()方法也能夠),咱們其實是在(將來將要)新建立的SubType實例的環境下調用了SuperType構造函數。這樣一來,就會在新SubType對象上執行SuperType()函數中定義的全部對象初始化代碼。結果,SubType的每一個實例就都會具備本身的colors屬性的副本了(互不影響)。函數
能夠在子類型構造函數中向超類型構造函數傳遞參數。以下this
function SuperType(name) { this.name = name; } function SubType() { //繼承了SuperType,同時還傳遞了參數 SuperType.call(this, "Nicholas"); //實例屬性 this.age = 29; } var instance = new SubType(); //"Nicholas"; alert(instance.name); //29 alert(instance.age);
SuperType只接受一個參數name,該參數會直接賦給一個屬性。在SubType構造函數內部調用SuperType構造函數時,其實是爲SubType的實例設置了name屬性。爲了確保SuperType構造函數不會重寫子類型的屬性,能夠在調用超類型構造函數後,再添加應該在子類型中定義的屬性。spa
1.方法都在構造函數中定義
2.在超類型的原型中定義的方法,對子類型而言也是不可見的 以下prototype
function SuperType(name) { this.name = name; } SuperType.prototype.a=function(){ alert("aaaa"); } function SubType() { //繼承了SuperType,同時還傳遞了參數 SuperType.call(this, "Nicholas"); //實例屬性 this.age = 29; } var instance = new SubType(); console.log(instance);
咱們在控制檯能夠看到子類型原型中沒法獲取超類型的a方法日誌
將原型鏈和借用構造函數的技術組合到一塊,從而發揮兩者之長的一種繼承模式,主要的思路是使用原型鏈實現對原型屬性和方法的繼承,而經過借用構造函數來實現對實例屬性的繼承。這樣,既經過在原型上定義方法實現了函數複用,又可以保證每一個實例都有它本身的屬性。下面來看一個例子code
function SuperType(name) { this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function() { alert(this.name); }; function SubType(name, age) { //繼承name屬性 SuperType.call(this, name); this.age = age; } //繼承方法 (拼接原型鏈) SubType.prototype = new SuperType(); SubType.prototype.sayAge = function() { alert(this.age); }; var instance1 = new SubType("Nicholas", 29); instance1.colors.push("black"); //"red,blue,green,black" alert(instance1.colors); //"Nicholas"; instance1.sayName(); //29 instance1.sayAge(); var instance2 = new SubType("Greg", 27); //"red,blue,green" alert(instance2.colors); //"27"; instance2.sayAge(); //"Greg"; instance2.sayName();
咱們看到如今實例能夠訪問的超類型的原型上的方法了
SuperType構造函數定義了兩個屬性:name和colors。SuperType的原型定義了一個方法sayName()。Sub-Type構造函數在調用SuperType構造函數時傳入了name參數,緊接着又定義了它本身的屬性age。而後,將SuperType的實例賦值給SubType的原型,而後又在該新原型上定義了方法sayAge()。這樣一來,就可讓兩個不一樣的SubType實例既分別擁有本身屬性——包括colors屬性,又可使用相同的方法了這種方式是目前js實現繼承使用的最多見的方式對象
SubType.prototype = new SuperType()的確會建立一個關聯到SubType.prototype 的新對象。可是它使用了SubType(..)的「構造函數調用」,若是函數SubType有一些反作用(好比寫日誌、修改狀態、註冊到其餘對象、給this添加數據屬性,等等)的話,就會影響到SubType()的「後代」。繼承
function SuperType(name) { this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function() { alert(this.name); }; function SubType(name, age) { //繼承name屬性 SuperType.call(this, name); this.age = age; } //使用Object.create 生成對象來代替new SuperType()生成的對象 SubType.prototype = Object.create(SuperType.prototype); SubType.prototype.sayAge = function() { alert(this.age); }; var instance1 = new SubType("Nicholas", 29); console.log(instance1 );
這樣能夠避免對SubType後代的影響
注
// ES6以前須要拋棄默認的SubType.prototype SubType.ptototype = Object.create( SuperType.prototype ); // ES6開始能夠直接修改現有的 SubType.prototypeObject.setPrototypeOf( SubType.prototype, SuperType.prototype );