為了解決包含引用類型值的原型屬性會被所有實例共享的問題,大神們發明了在子類型構造函數的內部調用超類型構造函數然後透過apply()和call( )方法在(將來)新建的物件上執行建構函式的方式來實作繼承,如下
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屬性的副本了(互不影響)。
可以在子類型建構子中向超類型建構函式傳遞參數。如下
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建構子不會重寫子類型的屬性,可以在呼叫超類型建構函式後,再加入應該在子類型中定義的屬性。
1.方法都在建構函式中定義
2.在超型別的原型中定義的方法,對子型別而言也是不可見的如下
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方法
將原型鍊和借用建構函式的技術組合到一塊,從而發揮二者之長的一種繼承模式,主要的思路是使用原型鏈實現對原型屬性和方法的繼承,而藉由借用建構函式來實現對實例屬性的繼承。這樣,既透過在原型上定義方法實現了函數復用,又能夠保證每個實例都有它自己的屬性。下面來看一個範例
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 );
相關推薦:
JavaScript高階程式設計閱讀筆記(十四) js繼承機制的實作_javascript技巧
以上是細說js繼承的詳細內容。更多資訊請關注PHP中文網其他相關文章!