js原型鏈講解,又細又干!

原型鏈的概念

在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。


截屏2021-02-03 下午2.16.06.png

這樣就可以訪問我父類中的屬性,如果我們以此類推,就可以讓多個類通過修改prototype來進行繼承修改,也就形成類原型鏈。
如果我們現(xiàn)在想執(zhí)行l(wèi)eader.name,那么會經(jīng)歷這三個步驟:

1.搜索實例
2.搜索Leader prototype
3.搜索Person prototype

object才是祖宗

在原型鏈中,不管如何繼承,都是繼承了object。就比如在調用toString()函數(shù)的時候,其實就是搜索的object中的toString()


截屏2021-02-03 下午2.25.35.png

除此之外,我們可以用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()
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容