JavaScript物件繼承方式
一、物件冒充
其原理如下:建構函式使用 this 關鍵字給所有屬性和方法賦值(即採用類宣告的建構函式方式)。因為建構函式只是一個函式,所以可使 Parent 建構函式 成為 Children 的方法,然後呼叫它。Children 就會收到 Parent 的建構函式中定義的屬性和方法。例如,用下面的方式定義 Parent 和 Children:
]// 父類建構函式 var Parent = function(name){ this.name = name; this.sayHi = function(){ console.log("Hi! " + this.name + "."); } }; // 子類建構函式 var Children = function(name){ this.method = Parent; this.method(name); // 實現繼承的關鍵 delete this.method; this.getName = function(){ console.log(this.name); } }; var p = new Parent("john"); var c = new Children("joe"); p.sayHi(); // 輸出: Hi! john. c.sayHi(); // 輸出: Hi! joe. c.getName(); // 輸出: jo
原理:就是把 Parent 建構函式放到 Children 建構函式裡面執行一次。那為什麼不直接執行,非要轉個彎把 Parent 賦值給 Children 的 method 屬性再執行呢? 這跟 this 的指向有關,在函式內 this 是指向 window 的。當將 Parent 賦值給 Children 的 method 時, this 就指向了 Children 類的例項。
二、原型鏈繼承
眾所周知,JavaScript 是一門基於原型的語言,在 JavaScript 中 prototype 物件的任何屬性和方法都被傳遞給那個類的所有例項。原型鏈利用這種功能來實現繼承機制:
// 父類建構函式 var Parent = function(){ this.name = "john"; this.sayHi = function(){ console.log("Hi! " + this.name + "."); } }; // 子類建構函式 var Children = function(){}; Children.prototype = new Parent(); // 實現繼承的關鍵 var p = new Parent(); var c = new Children(); p.sayHi(); // 輸出: Hi! john. c.sayHi(); // 輸出: Hi! john.
注意:呼叫 Parent 的建構函式,沒有給它傳遞引數。這在原型鏈中是標準做法。要確保建構函式沒有任何引數。
三、使用 call 或 applay 方法
這個方法是與物件冒充方法最相似的方法,因為它也是通過改變了 this 的指向而實現繼承:
// 父類建構函式
var Parent = function(name){
this.name = name;
this.sayHi = function(){
console.log("Hi! " + this.name + ".");
}
};
// 子類建構函式
var Children = function(name){
Parent.call(this, name); // 實現繼承的關鍵
this.getName = function(){
console.log(this.name);
}
};
var p = new Parent("john");
var c = new Children("joe");
p.sayHi(); // 輸出: Hi! john.
c.sayHi(); // 輸出: Hi! john.
c.getName(); // 輸出: joe
apply 方法本人就不舉列了,它和 call 方法的區別在於它的第二個引數必須是陣列。
四、混合方式
物件冒充的主要問題是必須使用建構函式方式,這不是最好的選擇。不過如果使用原型鏈,就無法使用帶引數的構造函數了。如何選擇呢?答案很簡單,兩者都用。 在 JavaScript 中建立類的最好方式是用建構函式定義屬性,用原型定義方法。這種方式同樣適用於繼承機制:
// 父類建構函式
var Parent = function(name){
this.name = name;
};
Parent.prototype.sayHi = function(){
console.log("Hi! " + this.name + ".");
};
// 子類建構函式
var Children = function(name, age){
Parent.call(this, name); // 實現繼承的關鍵
this.age = age;
};
Children.prototype = new Parent(); // 實現繼承的關鍵
Children.prototype.getAge = function(){
console.log(this.age);
};//前端全棧開發交流圈:866109386
//幫助1-3年前端人員突破技術瓶頸,提升思維
var p = new Parent("john");
var c = new Children("joe",30);
p.sayHi(); // 輸出: Hi! john.
c.sayHi(); // 輸出: Hi! joe.
c.getAge(); // 輸出: 30
五、使用Object.create 方法
Object.create 方法會使用指定的原型物件及其屬性去建立一個新的物件:
// 父類建構函式
var Parent = function(name){
this.name = name;
};
Parent.prototype.sayHi = function(){
console.log("Hi! " + this.name + ".");
};
// 子類建構函式
var Children = function(name, age){
Parent.call(this, name); // 實現繼承的關鍵
this.age = age;
};
Children.prototype = Object.create(Parent.prototype); // 實現繼承的關鍵
Children.prototype.constructor = children; // @
Children.prototype.getAge = function(){
console.log(this.age);
};
var p = new Parent("john");
var c = new Children("joe",30);
p.sayHi(); // 輸出: Hi! john.
c.sayHi(); // 輸出: Hi! joe.
c.getAge(); // 輸出: 30
@ 當執行 Children.prototype = Object.create(Parent.prototype) 這個語句後,Children 的 constructor 就被改變為 Parent ,因此需要將 Children.prototype.constructor 重 新指定為 Children 自身。
六、extends 關鍵字實現繼承
這個是 ES6 的語法糖,下面看下es6實現繼承的方法:
class Parent {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
class Children extends Parent {
constructor(name, age, job) {
this.job = job; // 這裡會報錯
super(name, age);
this.job = job; // 正確
}
}
上面程式碼中,子類的constructor方法沒有呼叫super之前,就使用this關鍵字,結果報錯,而放在super方法之後就是正確的。子類Children的建構函式之中的super(),代表呼叫父類Parent的建構函式。這是必須的,否則 JavaScript 引擎會報錯。
注意,super雖然代表了父類Parent的建構函式,但是返回的是子類Children的例項,即super內部的this指的是Children,因此super()在這裡相當於Parent.prototype.constructor.call(this)。