此貼用於記錄原型鏈相關的一些東西。
函數都有prototype,對象都有__proto__,一個函數的prototype和一個對象的__proto__就是原型,原型其實也是一個對象。
一個函數的prototype和這個函數的示例對象的__proto__是同一個引用,即:
function A(){
}
let a = new A();
console.log(a.__proto__ === A.prototype); //返回的是true
當調用某一個對象的方法的時候,如果找不到這個對象的方法,則會去找這個對象的原型裏的方法,如果再找不到,則繼續找原型的原型的方法,一直往上找,這個就是原型鏈,如果都找不到,則會報錯。
在引入es6的extends之前,函數的繼承就是通過原型來實現的。下面記錄一下我理解的繼承,下面的繼承參考的是[JS繼承的實現方式
][1],他那裏有完整的繼承方法。
一. 原型鏈繼承
function A(name) {
this.name = name;
this.f1 = function () {
console.log(this.name + '正在做f1');
}
}
A.prototype.f2 = function () {
console.log(this.name + '正在做f2');
}
function B(name) {
this.name = name;
}
B.prototype = new A('');
let b = new B('test');
b.f1();
b.f2();
console.log(b.__proto__ === B.prototype); //true
console.log(b instanceof A);//true
console.log(b instanceof B);//true
優點:
1.簡單,實現容易
2.能訪問父類所有的方法和屬性
3.實例是當前類的實例,也是父類的實例
缺點:
1.要給B的prototype新增方法必須要在 new A('');之後
2.無法實現多繼承
3.沒法向父類的構造函數傳遞參數
二. 構造繼承
function A(name){
this.name = name;
this.f1 = function(){
console.log(this.name + '正在做f1');
}
}
A.prototype.f2 = function(){
console.log(this.name + '正在做f2');
}
function B(name) {
A.call(this, name);
}
let b = new B('test');
b.f1();
// b.f2();// 會報錯
console.log(b instanceof A);//false
console.log(b instanceof B);//true
優點:
1.可以使用父類的屬性和方法
2.可以實現多重繼承,即可以call多個函數
3.實例對象是當前類的實例,不是父類的實例
缺點:
1.無法獲取A的prototype的屬性和方法
2.只是子類的實例,不是父類的實例
3.無法實現函數複用,每個子類都有父類實例函數的副本,影響性能(此處應該是調用call的時候會生成父類的實例副本,具體的還得再研究研究)
三. 組合繼承
function A(name){
this.name = name;
this.f1 = function(){
console.log(this.name + '正在做f1');
}
}
A.prototype.f2 = function(){
console.log(this.name + '正在做f2');
}
function B(name) {
A.call(this, name);
}
B.prototype = new A('');
//要修正prototype的constructor,爲什麼要修正還有待研究
B.prototype.constructor = B;
let b = new B('test');
b.f1();
b.f2();
console.log(b instanceof A);//true
console.log(b instanceof B);//true
優點:
1.包含了原型鏈繼承和構造繼承的優點
2.解決了原型鏈繼承的無法實現多繼承和沒法向父類的構造函數傳遞參數的缺點
3.解決了構造繼承無法獲取A的prototype的屬性和方法還有隻是子類的實例,不是父類的實例的問題
缺點:
1.調用了兩次父類構造函數,生成了兩份實例(子類實例將子類原型上的那份屏蔽了)
四.寄生組合繼承
function A(name){
this.name = name;
this.f1 = function(){
console.log(this.name + '正在做f1');
}
}
A.prototype.f2 = function(){
console.log(this.name + '正在做f2');
}
function B(name) {
A.call(this, name);
}
(function(){
// 創建一個沒有實例方法的類
var Super = function(){};
Super.prototype = A.prototype;
//將實例作爲子類的原型,這樣就可以只獲取A的prototype的屬性了
B.prototype = new Super();
//
B.prototype.constructor = B;
})();
let b = new B('test');
b.f1();
b.f2();
console.log(b instanceof A);//true
console.log(b instanceof B);//true
優點:
1.在組合繼承的基礎上,只生成了一份父類的實例,prototype也只繼承了父類的prototype,沒有繼承私有的屬性
缺點:
1.實現複雜