學習一波阮一峰的部落格 戳這裡
部落格中是自己的理解,以及對大佬描述不清楚的地方進行了修正,也算是自己的一個再(xiao)產(tu)出(cao)吧
建構函式進行繼承
先來看個簡單的:
function Animal(){
this.type = `animal;`
}
function Cat(name, color){
this.name = name
this.color = color
//這裡用call,個人覺得更好些
Animall.call(this)
}
var cat = new Cat(`po`, `orange`)
console.log(cat.type) //animal
複製程式碼
建立了一個Animal和Cat建構函式,然後在Cat裡面呼叫Animal的建構函式,在將Cat例項化,就可以訪問到Animal的屬性了。
這個例子顯然有問題,放在第一個就是用來找茬的。
什麼問題呢?假如我想使用在Animal上的公共方法,像這樣Animal.prototype.eat = function(){ console.log(`animal eat`) }
,用cat.eat()
是訪問不到的。
為什麼呢?因為我們Cat和Animal的原型根本就沒有關聯起來呀。你看看我們們上面的程式碼,那個地方關聯過?
使用原型進行繼承
那下面我們就將兩者的原型關聯起來試試看
function Animal(){
this.type = `animal;`
}
Animal.prototype.eat = function(){ console.log(`animal eat`) }
function Cat(name, color){
this.name = name
this.color = color
}
Cat.prototype = new Animal()
var cat = new Cat(`po`, `orange`)
console.log(cat.type) //animal
console.log(cat.eat()) //animal eat
複製程式碼
這種方法好!可以拿到屬性和方法,一舉兩得。但是,這裡有個陷阱(keng)!!!Cat.prototype = new Animal()
之後,我們的Cat.prototype裡面的所有方法都消失了!這是怎麼回事?因為new,new做了四件事,這裡再次回顧一下:
var temp = {}
temp.__proto__ = Animal.prototype
Animal.call(temp)
return temp
複製程式碼
看到了嗎?new會使用一個空物件並且將其返回。這樣一來我們的Cat.prototype就被清空了(包括自身的constructor)。所以我們需要自己多做一件事Cat.prototype.constructor = Cat
。
如果我們不這樣做,那麼Cat.prototype的建構函式會是什麼?我們在去看看new做的第二件事,這個空物件指向了Animal.prototype,所以Cat.prototype自身如果沒有constructor屬性的話就會去Animal.prototype上面去找。Cat.prototype.constructor === Animal //true
所以,如果我們替換了prototype,需要手動去糾正它。
直接繼承prototype
既然上面這種方法有坑,而且的的確確讓你很容易漏掉,那我們改進一下:
function Animal(){}
Animal.prototype.eat = function(){ console.log(`animal eat`) }
Cat.prototype = Animal.prototype
Cat.prototype.constructor = Cat
var cat = new Cat(`po`, `orange`)
console.log(cat.eat()) //animal eat
複製程式碼
既然我們想從Animal.prototype上面那東西,直接從上面拿不就行了?而且我還機智的填了上面會出現的坑。同時結果也是我想要的。
但是!!我們的Cat.prototype和Animal.prototype指向的是同一個原型,這會導致我在Cat.prototype上做了什麼事,會同時發生在Animal.prototype上。這是為什麼呢?MDZZ,這兩個就是同一個東西呀,原型是堆中一塊記憶體,Cat和Animal都指向這塊記憶體,操作的是同一個東西,怎麼會不影響?
與此同時,自以為聰明的填坑Cat.prototype.constructor = Cat
,此時的Cat和Animal的原型是同一個,修改了constructor之後,導致Animal的constructor變成了Cat。這種方法果斷PASS。。。
利用空物件作為中介
var F = function(){}
F.prototype = Animal.prototype
Cat.prototype = new F()
Cat.prototype.constructor = Cat
複製程式碼
我們使用中間物件進行過度,巧妙的將Cat.prototype和Animal.prototype解耦,這樣就不會出現問題了。仔細觀察會發現這個和new做的事情有異曲同工之處。
我們進行封裝一下在使用
function extend(Child, Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.uber = Parent.prototype;
}
extend(Cat,Animal);
var cat1 = new Cat(`po` `orange`);
alert(cat1.eat()); // animal eat
複製程式碼
這裡Child.uber = Parent.prototype
的意思類似於__proto__
。
拷貝繼承
這裡還有一種簡單粗暴的方式
function extend2(Child, Parent) {
var p = Parent.prototype;
var c = Child.prototype;
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
}
function Animal(){}
Animal.prototype.eat = function(){ console.log(`animal eat`) }
extend2(Cat, Animal);
var cat1 = new Cat(`po` `orange`);
alert(cat1.eat()); // animal eat
複製程式碼
直接遍歷父類的原型,一個個複製到子類原型上即可。
感慨一下,大佬還是大佬呀。。。