原型鏈的概念
在oo語言中都會談到繼承這一要素,js原型鏈的出現(xiàn)解決了繼承的問題。
簡單回顧一下構造函數(shù)、原型和實例的關系:
每個構造函數(shù)都有一個原型對象
原型對象都包含一個指向構造函數(shù)的指針
實例都包含一個指向原型對象的內部指針
function Person(){
this.name='xihongshidalumao'
}
let person = new Person()
就比如上面這個例子,person實例和Person對象的prototype指向Person prototype, 而Person prototype中的構造函數(shù)又重新指會Person對象。
那么我們現(xiàn)在將代碼進行如下修改:
function Person(){
this.name='xihongshidalumao'
}
let person = new Person()
console.log(person)
function Leader(){
this.position='boss'
}
// 繼承
Leader.prototype = new Person()
let leader = new Leader()
console.log(leader)
新創(chuàng)建一個Leader對象,然后將對象的prototype指向new Person,這樣就修改了Leader prototype中的consructor指向了Person protytype。

這樣就可以訪問我父類中的屬性,如果我們以此類推,就可以讓多個類通過修改prototype來進行繼承修改,也就形成類原型鏈。
如果我們現(xiàn)在想執(zhí)行l(wèi)eader.name,那么會經(jīng)歷這三個步驟:
1.搜索實例
2.搜索Leader prototype
3.搜索Person prototype
object才是祖宗
在原型鏈中,不管如何繼承,都是繼承了object。就比如在調用toString()函數(shù)的時候,其實就是搜索的object中的toString()

除此之外,我們可以用instanceof和isPrototypeof這兩種方法來確定這個關系。
console.log(leader instanceof Object) // ture
console.log(Object.prototype.isPrototypeOf(leader)) // ture
原型鏈的問題
原型鏈固然強大,但是所有的實例都會共享原型中的屬性,所以在定義數(shù)據(jù)的時候最好在構造函數(shù)中定義。
function Person(){
this.color = ["red","yellow"]
}
function Dog(){}
let person = new Person()
Dog.prototype = new Person()
let dog = new Dog()
let dog2 = new Dog()
dog.color.push("green")
console.log(dog.color) //["red", "yellow", "green"]
console.log(dog2.color) //["red", "yellow", "green"]
第二個問題就是,在原型鏈中,子類無法通過父類的構造函數(shù)傳遞參數(shù)。
function Person(color){
this.color = color
}
function Dog(){}
let person = new Person(["red", "yellow", "green"])
Dog.prototype = new Person(["red", "yellow"])
let dog = new Dog()
let dog2 = new Dog()
console.log(person.color) //["red", "yellow", "green"]
console.log(dog.color) //["red", "yellow"]
console.log(dog2.color) //["red", "yellow"]
通過構造函數(shù)傳參,person 和 Dog分別new 了兩個Person,導致person.color和dog.color出現(xiàn)不同的結果,但是dog和dog2之間的結果是相同的。所以為了解決這一個問題,提出了借用構造函數(shù)。
借用構造函數(shù)
其實原理很簡單,就是在子類中調用一次父類的構造函數(shù)。一般是通過apply和call綁定this這一方式進行。
function Person(color){
this.color = color
}
function Dog(color){
Person.call(this,color)
}
Dog.prototype = new Person()
let dog = new Dog(["red", "yellow", "green"])
let dog2 = new Dog(["red", "yellow"])
console.log(dog.color) // ["red", "yellow", "green"]
console.log(dog2.color) // ["red", "yellow"]
當然借用構造函數(shù)也有他的問題,由于方法都在構造函數(shù)中定義,所以導致無法對方法進行函數(shù)復用。在超類型的原型中定義的方法,對子類型而言也是不可見的,結果所有類型都只能使用構造函數(shù)模式。
組合繼承
只要思想不滑坡,辦法總比困難多,由于上面出現(xiàn)的問題,程序猿們又發(fā)明出了組合繼承,其實就是是將原型鏈和借用構造函數(shù)的技術組合到一塊。
不廢話,上??。
function Person(name){
this.name = name
this.has = ["leg","head","body","arm"]
this.sayName = function(){
console.log(this.name)
}
}
function Boss(name){
Person.call(this,name)
}
Boss.prototype = new Person()
Boss.prototype.constructor = Boss
Boss.prototype.sayHas = function(){
console.log(this.has)
}
let boss1 = new Boss("boss")
let boss2 = new Boss("little boss")
console.log(boss1.name)
console.log(boss2.name)
boss1.has.push("old")
boss2.has.push("little")
console.log(boss1.has)
console.log(boss2.has)
boss1.sayName()
boss2.sayName()
boss1.sayHas()
boss2.sayHas()