原型鏈

什么是原型?

 function Person(){
     
 }
 let p = new Person()

實例對象擁有__proto__
函數(shù)擁有Person.prototype,Person.prototype就是原型,它是一個對象,我們也稱它為原型對象。

什么是原型鏈

原型與原型層層相鏈接的過程即為原型鏈。

Object.prototype.__proto__ // null
Function.prototype.__proto__ === Object.prototype // true
Function.__proto__ === Function.prototype // true
image
 function Person(params) {
            
        }
        Person.prototype.n = 1
        Person.prototype.add = function () {
            this.n++
        }
        let p1 = new Person()
        let p2 = new Person()
        p1.add()
        p2.add()
        console.log(Person.prototype.n) // 1

p1.add()執(zhí)行時,addthis的指向為p1這個對象實例,當(dāng)p1的屬性找不到n時,通過原型鏈向上查找,找到n=1,然后給當(dāng)前對象p1進(jìn)行賦值

原型繼承

原型模式

        function Person() {
            this.name = '小王'
            this.color = ['red', 'black']
            this.getName = function () {
                console.log(this.name);
            }
        }


        function Man() {
            this.name = '小黃'
        }

        Man.prototype = new Person()
      
        const man = new Man()
        const man2 = new Man()

        man.color.push('green')

        console.log(man.color); // ['red', 'black', 'green']
        console.log(man2.color); // ['red', 'black', 'green']

兩個問題:

  • 原型中的引用值會在多個實例間共享
  • 子類型在實例化時不能給夫類型的構(gòu)造函數(shù)傳參

盜用構(gòu)造函數(shù)(對象偽裝 或 經(jīng)典繼承)

        function Person() {
            this.name = '小王'
            this.color = ['red', 'black']
            this.getName = function () {
                console.log(this.name);
            }
        }


        function Man() {
            Person.call(this)
            this.name = '小黃'
        }

      
        const man = new Man()
        const man2 = new Man()

        man.color.push('green')
        
        console.log(man.color); // ['red', 'black', 'green']
        console.log(man2.color); // ['red', 'black']

通過使用call()(或apply())方法,Person的構(gòu)造函數(shù)在Man的實例創(chuàng)建的上下文中執(zhí)行了。相當(dāng)于新的Man對象上運行了Person函數(shù)中的所有初始化代碼,結(jié)果就是每個實例都會有自己的colors屬性。

優(yōu)點

  • 能夠在子類構(gòu)造函數(shù)中像父類構(gòu)造函數(shù)傳參

問題

  • 必須在構(gòu)造函數(shù)中定義方法,因此函數(shù)不能重用
  • 子類也不能訪問父類原型上定義的方法,因此所有類型只能使用構(gòu)造函數(shù)模式

組合繼承(偽經(jīng)典繼承)

綜合原型鏈和盜用構(gòu)造函數(shù),結(jié)合兩者的優(yōu)點。

思路是使用原型鏈繼承原型上的屬性和方法,通過盜用構(gòu)造函數(shù)繼承實例屬性

        function Person() {
            this.name = '小王'
            this.color = ['red', 'black']
            this.getName = function () {
                console.log(this.name);
            }
        }


        function Man() {
            // 繼承構(gòu)造函數(shù)實例的屬性
            Person.call(this)
            this.name = '小黃'
        }

        // 繼承原型的屬性和方法
        Man.prototype = new Person()
        
        const man = new Man()
        const man2 = new Man()

        man.color.push('green')
        
        console.log(man.color); // ['red', 'black', 'green']
        console.log(man2.color); // ['red', 'black']

組合繼承彌補(bǔ)了原型鏈和盜用構(gòu)造函數(shù)的不足,是JavaScript中使用最多的繼承模式。而且組合繼承也保留了instanceof操作符和isPrototypeOf()方法識別合成對象的能力。

原型式繼承

function object(o) {
            function F() {}
            F.prototype = o;
            return new F()
        }

object會創(chuàng)建一個臨時構(gòu)造函數(shù),將傳入的對象賦值給這個構(gòu)造函數(shù)的原型,然后返回這個臨時構(gòu)造函數(shù)的實例。

實質(zhì)上是對傳入對象的一次淺復(fù)制,所以還存在值引用的問題。

        let obj = {
            age: 23,
            height: 180,
            skill: ['java', 'js']
        }
        const man = object(obj)
        const man1 = object(obj)
        // es5新增了Object.ceate的方法可替代
        const man = Object.create(obj)
        const man1 = Object.create(obj)
        
        man.skill.push('c++')
        man.skill; // ["java", "js", "c++"]
        man1.skill;// ["java", "js", "c++"]

原型式繼承非常適合不需要單獨創(chuàng)建構(gòu)造函數(shù),但仍然需要在對象間共享信息的場合。但要記住,屬性中包含的引用值始終會在相關(guān)對象間共享,跟使用原型模式是一樣的。

寄生式繼承

寄生式繼承背后的思路類似于寄生構(gòu)造函數(shù)和工廠模式:創(chuàng)建一個實現(xiàn)繼承的函數(shù),以某種方式增強(qiáng)對象,然后返回這個對象。

        function createAnother(original) {
            // 通過調(diào)用函數(shù)創(chuàng)建一個新的對象
            let clone = object(original);
            // 以某種方式增強(qiáng)這個對象
            clone.sayHi = function () {
                console.log('Hi');
            }
            // 返回這個對象
            return clone
        }

        let person = {
            name: 'tom',
            friends: ['jerry', 'arthur']
        }
        let anoherPerson = createAnother(person);
        anoherPerson.sayHi()

寄生式繼承同樣適合主要關(guān)注對象,而不在乎類型和構(gòu)造函數(shù)的場景。object()函數(shù)不是寄生式繼承所必需的,任何返回新對象的函數(shù)都可以在這里使用。

寄生式組合繼承

組合繼承其實也存在效率問題。最主要的效率問題就是父類構(gòu)造函數(shù)始終會被調(diào)用兩次:一次在是創(chuàng)建子類原型時調(diào)用,另一次是在子類構(gòu)造函數(shù)中調(diào)用。

寄生式組合繼承通過盜用構(gòu)造函數(shù)繼承屬性,但使用混合式原型鏈繼承方法?;舅悸肥遣煌ㄟ^調(diào)用父類構(gòu)造函數(shù)給子類原型賦值,而是取得父類原型的一個副本。說到底就是使用寄生式繼承來繼承父類原型,然后將返回的新對象賦值給子類原型。

  function Person() {
            this.name = '小王'
            this.color = ['red', 'black']
            this.getName = function () {
                console.log(this.name);
            }
        }

        Person.prototype.code = function () {
            console.log('coding');
        }

        Person.prototype.skill = ['java', 'js']

        function Man() {
            Person.call(this)
        }


        function inheritPrototype(man, person) {
            const prototype = person.prototype
            prototype.constructor = person;
            man.prototype = prototype
        }

        inheritPrototype(Man, Person)
        const man = new Man()

寄生式組合繼承可以算是引用類型繼承的最佳模式。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、理解原型設(shè)計模式以及 JavaScript 中的原型規(guī)則 設(shè)計模式 工廠模式 在函數(shù)內(nèi)創(chuàng)建一個對象,給對象賦予...
    旭哥_閱讀 684評論 0 1
  • 創(chuàng)建對象 1、工廠模式: 通過函數(shù)的形式傳入?yún)?shù),返回必要的信息的Person 對象,可以多次調(diào)用。工廠模式解決了...
    Lyan_2ab3閱讀 699評論 0 1
  • 原型: 在講原型關(guān)系之前給我們來看一張圖片: 由圖我們可知幾個關(guān)系: 每一個構(gòu)造函數(shù)都有(原型)prototype...
    尋夢人_b67c閱讀 1,851評論 0 2
  • 1.理解原型設(shè)計模式以及JavaScript中的原型規(guī)則 2.instanceof的底層實現(xiàn)原理,手動實現(xiàn)一個in...
    無了無了閱讀 405評論 0 0
  • 面向?qū)ο蟮恼Z言支持兩種繼承方式,接口繼承和實現(xiàn)繼承js無法實現(xiàn)接口繼承,只支持實現(xiàn)繼承,主要通過原型鏈來實現(xiàn)。具體...
    jadefan閱讀 161評論 0 2

友情鏈接更多精彩內(nèi)容