直接進入主題:javascript
繼承的操做須要有一個父類,這裏使用構造函數外加原型來建立一個:html
// super
function Person(name){
this.name = name;
}
Person.prototype.job = 'frontend';
Person.prototype.sayHello = function() {
console.log('Hello '+this.name);
}
var person = new Person('jia ming');
person.sayHello(); // Hello jia ming
複製代碼
// 原型鏈繼承
function Child() {
this.name = 'child';
}
Child.prototype = new Person();
var child = new Child();
console.log(child.job); // frontend
// instanceof 判斷元素是否在另外一個元素的原型鏈上
// child是Person類的實例
console.log(child instanceof Person); // true
複製代碼
關鍵點:子類原型等於父類的實例Child.prototype = new Person()
java
原型鏈的詳細講解本身以前有一篇文章說到深刻理解原型對象和原型鏈git
特色:github
注意事項:app
// 借用構造函繼承
function Child() {
Person.call(this, 'reng');
}
var child = new Child();
console.log(child.name); // reng
console.log(child instanceof Person); // false
child.sayHello(); // 報錯,繼承不了父類原型上的東西
複製代碼
關鍵點:用call
或apply
將父類構造函數引入子類函數(在子類函數中作了父類函數的自執行(複製))Person.call(this, 'reng')
frontend
針對call, apply, bind
的使用,以前有篇文章談談JavaScript中的call、apply和bind提到。函數
特色:post
注意事項:ui
組合繼承是原型鏈繼承和借用構造函數繼承
的組合。
// 組合繼承
function Child(name) {
Person.call(this, name);
}
Child.prototype = new Person();
var child = new Child('jia');
child.sayHello(); // Hello jia
console.log(child instanceof Person); // true
複製代碼
關鍵點:結合了兩種模式的優勢--向父類傳參(call)和複用(prototype)
特色:
注意事項:
// 先封裝一個函數容器,用來承載繼承的原型和輸出對象
function object(obj) {
function F() {}
F.prototype = obj;
return new F();
}
var super0 = new Person();
var super1 = object(super0);
console.log(super1 instanceof Person); // true
console.log(super1.job); // frontend
複製代碼
關鍵點:用一個函數包裝一個對象,而後返回這個函數的調用,這個函數就變成了能夠隨意增添屬性的實例或對象。Object.create()
就是這個原理。
特色:
注意事項:
**Object.create()方法規範了原型式繼承。**這個方法接收兩個參數,一個用做新對象原型的對象和(可選的)一個爲新對象定義額外屬性的對象。
// 傳一個參數的時候
var anotherPerson = Object.create(new Person());
console.log(anotherPerson.job); // frontend
console.log(anotherPerson instanceof Person); // true
複製代碼
// 傳兩個參數的時候
var anotherPerson = Object.create(new Person(), {
name: {
value: 'come on'
}
});
anotherPerson.sayHello(); // Hello come on
複製代碼
function object(obj) {
function F(){}
F.prototype = obj;
return new F();
}
var sup = new Person();
// 以上是原型式繼承,給原型式繼承再套個殼子傳遞參數
function subobject(obj) {
var sub = object(obj);
sub.name = 'ming';
return sub;
}
var sup2 = subobject(sup);
// 這個函數通過聲明後就成了可增添屬性的對象
console.log(sup2.name); // 'ming'
console.log(sup2 instanceof Person); // true
複製代碼
關鍵點:就是給原型式繼承外面套個殼子。
特色:
注意事項:
它跟組合繼承同樣,都比較經常使用。
寄生:在函數內返回對象而後調用
組合:
// 寄生
function object(obj) {
function F(){}
F.prototype = obj;
return new F();
}
// object是F實例的另外一種表示方法
var obj = object(Person.prototype);
// obj實例(F實例)的原型繼承了父類函數的原型
// 上述更像是原型鏈繼承,只不過只繼承了原型屬性
// 組合
function Sub() {
this.age = 100;
Person.call(this); // 這個繼承了父類構造函數的屬性
} // 解決了組合式兩次調用構造函數屬性的特色
// 重點
Sub.prototype = obj;
console.log(Sub.prototype.constructor); // Person
obj.constructor = Sub; // 必定要修復實例
console.log(Sub.prototype.constructor); // Sub
var sub1 = new Sub();
// Sub實例就繼承了構造函數屬性,父類實例,object的函數屬性
console.log(sub1.job); // frontend
console.log(sub1 instanceof Person); // true
複製代碼
重點:修復了組合繼承的問題
在上面的問題中,你可能發現了這麼一個註釋obj.constructor = Sub; // 必定要修復實例
。爲何要修正子類的構造函數的指向呢?
由於在不修正這個指向的時候,在獲取構造函數返回的時候,在調用同名屬性或方法取值上可能形成混亂。好比下面:
function Car() { }
Car.prototype.orderOneLikeThis = function() { // Clone producing function
return new this.constructor();
}
Car.prototype.advertise = function () {
console.log("I am a generic car.");
}
function BMW() { }
BMW.prototype = Object.create(Car.prototype);
BMW.prototype.constructor = BMW; // Resetting the constructor property
BMW.prototype.advertise = function () {
console.log("I am BMW with lots of uber features.");
}
var x5 = new BMW();
var myNewToy = x5.orderOneLikeThis();
myNewToy.advertise(); // => "I am BMW ..." if `BMW.prototype.constructor = BMW;` is not
// commented; "I am a generic car." otherwise.
複製代碼
《JavaScript高級程序設計》
更多的內容,請移步個人博客,能給個贊就更好了😄