js的是利用原型鏈實現物件導向的類行為,和oo語言中的類有這巨大的不同。
下面講述幾種繼承的方式以及一些擴充套件。
我對js的原型繼承的一些理解:
- js中只有函式存在prototype這個屬性,代表原型,object中只有__proto__屬性
- js中函式也是object的一種,所以函式同時存在prototype和__proto__屬性,但是這是兩個完全不同的屬性
- js中所有的object都是繼承於底層的Object.prototype
- js中所有函式的建構函式是Function
- Function.prototype是函式(這就是解決原型鏈迴圈的關鍵),正常函式的prototype屬性都是一個object
- js中例項的__proto__總是指向它建構函式的prototype(這個也是原型鏈的重點)
// 父類
function Animal() {
this.name = 'animal';
this.type = 'animal';
}
Animal.prototype.say = function () {
console.log(this.name);
}
複製程式碼
原型鏈繼承, 通過子類的prototype是父類的一個例項,從而實現一條原型鏈,如下:(Sub代表子類,sub代表子類的一個例項,Super代表父類,super代表父類的例項)
sub.__proto__ = Sub.prototype = super, super.__proto__ = Super.prototype 複製程式碼
缺點:父類的資料物件處於子類的prototype中,prototype是所有子類例項所共有的,所以對於引用型別的資料存在一改全改的問題
function Dog() {
this.name = 'dog'
}
Dog.prototype = new Animal();
var dog = new Dog();
dog.say();
複製程式碼
dog的詳細結構
為了解決上面的原型鏈缺點,利用建構函式的優點(組合繼承)
function Cat(data) {
Animal.call(this, data); // 將父類的資料型別在子類複製一份
this.name = 'cat';
}
Cat.prototype = new Animal();
var cat = new Cat(); // 建構函式再次儲存了一份父類的資料型別
cat.say();
複製程式碼
cat詳細結構,可以發現有個缺點就是父類的資料型別被儲存了2遍
ES5中有個api:
Object.create(object1, object2): 返回一個object,object的prototype = object1,object2代表prototype中新增或者覆蓋的屬性
eg:
var cat = Object.create(new Animal(), {
name: {
value: 'cat'
}
})
複製程式碼
上述cat詳細結構
參考紅寶書(js高階程式設計):這種方式稱為原型式繼承function object(o) {
function F(){}
F.prototype = o;
return new F();
}
複製程式碼
結合原型式繼承和建構函式,可以有如下的繼承方式
function Bird(data) {
Animal.call(this, data);
this.name = 'bird';
}
Bird.prototype = Object.create(Animal.prototype);
Bird.prototype.constructor = Bird;
var bird = new Bird();
bird.say();
複製程式碼
bird的詳細結構:解決了原型鏈繼承和組合繼承的問題
建構函式有個特點,就是return的是一個物件的時候new的時候生成的就是這個物件,否則將按照正常的原型鏈生成物件。eg:
function Test() {
var arr = [1, 2, 3];
this.arr = arr;
return arr;
}
Test.prototype.say = function() {
console.log(this.arr);
}
var test = new Test();
test.say // undefined
複製程式碼
這就是傳說中的寄生繼承,寄生繼承返回的物件跟原型鏈基本保持一致,唯一的問題就是丟失了子類的建構函式吧,以及第一層prototype
function Horse() {
var o = Object.create(new Animal());
o.name = 'horse';
return o;
}
var horse = new Horse();
horse.say();
複製程式碼
horse詳細結構:
擴充套件題目: 如何繼承陣列?
// 實現陣列繼承
var push = [].push;
function myArray() {
var array = Array.apply(this, arguments);
array.__proto__ = myArray.prototype;
return array;
}
myArray.prototype = Object.create(Array.prototype)
myArray.prototype.constructor = myArray;
myArray.prototype.push = function(value) {
console.log('push:', value);
push.call(this, value);
}
var list = new myArray(1, 2, 3);
list.push(4);
複製程式碼
因為陣列的獨特性所以利用了寄生繼承以及object的__proto__屬性,以及原型式繼承