js繼承的方式總結
- 對象冒充
- call()函數
- apply()函數
- 原型鏈
- 混合方式
對象冒充
function Father(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
this.say=function(){
alert('hello world');
}
}
function Son(name,age,sex){
this.extend=Father;//將構造方法當成普通方法使用,賦值給屬性
this.extend(name,age,sex);//調用
delete this.extend;//刪除對Father的引用
//所有新屬性和新方法都必須在刪除了新方法的代碼行後定義。否則,可能會覆蓋超類的相關屬性和方法,因爲this.extend創建了對Father的引用
}
var son=new Son('son',18,'男');
var father=new Father('father',38,'男');
console.log(son.name);//son
console.log(father.name);//father
實現原理:讓父類的構造函數成爲子類的方法,然後調用該子類的方法,通過this關鍵字給所有的屬性和方法賦值。
該種實現方式可以實現多繼承。
這裏存在一個弊端,如果存在兩個類 ClassA 和 ClassB 具有同名的屬性或方法,ClassB 具有高優先級。因爲它從後面的類繼承。除這點小問題之外,用對象冒充實現多重繼承機制輕而易舉。
call()函數
function Father(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
this.say=function(){
alert('hello world');
}
}
function Son(name,age,sex,grade){
Father.call(this,name,age,sex);
this.grade=grade;
}
var son=new Son('key',18,'男',100);
console.log(son.name);//key
console.log(son.grade);//100
實現原理:改變函數內部的函數上下文this,使它指向傳入函數的具體對象。
該種方式不能繼承原型鏈,若想繼承原型鏈,則採用第5種混合模式。
apply()函數
function Father(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
this.say=function(){
alert('hello world');
}
}
function Son(name,age,sex,grade){
Father.apply(this,[name,age,sex]);//參數以數組的形式
this.grade=grade;
}
var son=new Son('key',18,'男',100);
console.log(son.name);//key
console.log(son.grade);//100
實現原理:改變函數內部的函數上下文this,使它指向傳入函數的具體對象。
該種方式不能繼承原型鏈,若想繼承原型鏈,則採用5混合模式,與call()函數的區別就是參數是以數組的形式傳人。
原型鏈
function Father(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
this.say=function(){
alert(this.name);
}
}
function Son(grade){
this.grade=grade;
}
/*調用 Father 的構造函數,沒有給它傳遞參數。這在原型鏈中是標準做法。要確保構造函數沒有任何參數。
與對象冒充相似,子類的所有屬性和方法都必須出現在 prototype 屬性被賦值後,因爲在它之前賦值的所有方法都會被刪除。爲什麼?因爲 prototype 屬性被替換成了新對象,添加了新方法的原始對象將被銷燬。*/
Son.prototype=new Father();//直接將實例賦值給子類原型
var son=new Son(100);
son.name="key";
console.log(son.name);//key
son.say();//key
實現原理:使子類原型對象指向父類的實例以實現繼承,即重寫類的原型,弊端是不能直接實現多繼承。
混合方式
function Father(name) {
this.name = name;
}
Father.prototype.say = function () {
alert(this.name);
};
function Son(name, age) {
Father.call(this, name);
this.age = age;
}
Son.prototype = new Father();
Son.prototype.sayAge = function () {
alert(this.age);
};
var father = new Father("father");
var son = new Son("son", 18);
father.say();//father
son.say();//son
son.sayAge();//18
以下是w3cSchool中的原話,我覺得寫的很好!
對象冒充的主要問題是必須使用構造函數方式,這不是最好的選擇。不過如果使用原型鏈,就無法使用帶參數的構造函數了。開發者如何選擇呢?答案很簡單,兩者都用。
我們曾經講解過創建類的最好方式是用構造函數定義屬性,用原型定義方法。這種方式同樣適用於繼承機制,用對象冒充繼承構造函數的屬性,用原型鏈繼承 prototype 對象的方法。
目錄
瀏覽器兼容
- 所有主流瀏覽器都兼容。