JS核心知識點梳理——原型、繼承(下)

clipboard.png

引言

正如上篇所提到的,有些人認爲JavaScript並不是真正的面嚮對象語言,在經典的面嚮對象語言中,您可能傾向於定義類對象,然後您可以簡單地定義哪些類繼承哪些類,JavaScript使用了另一套實現方式,繼承的對象函數並不是通過複製而來,而是通過原型鏈繼承(通常被稱爲 原型式繼承 —— prototypal inheritance)。

繼承的方式

說到繼承,首先得明白繼承的是什麼東西。爲了方便理解,我個人把屬性(包括方法)分爲共有屬性和私有屬性。私有屬性比如名字或者身份證之類的,是每個人獨有的。共有屬性是能共用的屬性或者方法,比如愛好屬性,吃飯方法。

私有屬性的繼承(構造函數+call)

這個比較簡單,私有屬性用利用構造函數和call或者apply

const Person = function (name) {
        this.name = name
    }
const Students = function (name) {
        Person.call(this,name)
    }
const xm = new Students('小明')
console.log(xm)  //Students {name: "小明"}

公有屬性的繼承

這裏裏面坑有點多,大家聽我娓娓道來。
通過原型鏈實現公有屬性繼承肯定沒錯,但是我們設計的時候有個原則 子類需要有自己的原型,父類也必須要有自己的原型,子實例在自己的原型上找不到屬性的時候纔會到父原型上去找
子類.prototype = 父類.prototype 這樣肯定不行,雖然能繼承父類原型的方法,但是子類的原型和父類的原型是同一個,給子類原型添加方法的時候,相當於給父類的原型也添加了一個方法。so我們應該有一個緩衝

子類實例---->子類原型------->中間對象------->父類原型 //沿着箭頭能訪問,表現上符合我們的設計原則

最常見的是使用父類的實例當這個中間對象。

Children.prototype = new Parent()    

但是了這麼做有個不好的地方。會實例化一次父類。如果父類特別複雜,比如axios,那麼會帶來很多額外的開銷。

我們看一下中間對象有什麼作用,實際上只起了一個隔離和原型重定向的作用。完全可以用一個空對象實現這個功能

//實現緩衝
var fn = function() {}
fn.prototype = Parent.prototype
Children.prototype = new fn()

實際上,這個就是Oject.create()的實現

//Oject.create()
Object.create = function(obj){
    var fn = funcion(){}
    fn.prototype = obj
    reurturn new fn() 
}

終極繼承解決方案

現在既要繼承私有屬性,又要繼承公有屬性。

    const Person = function (name) {
        this.name = name
    }
    Person.prototype.eat = function () {
        console.log('i am hungry,i want to eat!')
    }
    const Student = function (name) {
        Person.call(this,name)
    }
    Student.prototype = Object.create(Person.prototype)   //注意這裏!原型重定向的後遺症
    const xm = new Student ('小明')
    xm.name //'小明' 
    xm.eat() //i am hungry,i want to eat!
    console.log(xm)
    //Student {name: "小明"}
    //  name: "小明"
    //  __proto__: Person
    //      __proto__: //這一層是個空對象,只有一個__proto__屬性指向Person的原型
    //          eat: ƒ ()
    //            constructor: ƒ (name)
    //            __proto__: Object

但是這裏 xm.constructor.name // Person
這裏因爲原型重定向後沒有重置construtor,xm本身沒有construtor只能找我們創建的空對象,空對象沒有construtor所以繼續往上找,找到了Person.prototype上的construtor爲 Person
所以解決思路很簡單,在改寫原型鏈的時候重置一下constructor就行了

...
var temObj =  Object.create(Person.prototype)
temObj.constructor = Student
Student.prototype =temObj
...
 xm.constructor.name // Student  ok搞定  

new幹了啥

既然new在“類”的創建裏面必須使用,那麼我們就說一下new到底幹了啥事情

1.創建一個對象o繼承構造函數
2.讓構造函數的this變爲o,並執行構造函數,將返回值設置爲k
3.如果k

//仿寫new
function new1(func) {
        var o = Object.create(func.prototype)
        var k = func.apply(o,arguments[1])
        return typeof k === 'object'? k: o
    }
const x = new1(Student,['張三'])
x.name //'張三'
x.eat //'i am hungry,i want to eat!'

我們回過頭再分析一下構造函數模式繼承

const Person = function (name) {
        this.name = name
    }
const Students = function (name) {
        Person.call(this,name) //this是student實例
    }
const xm = new Students('小明')  //分析這裏幹了什麼
console.log(xm)  //Students {name: "小明"}

1.讓空對象o繼承Students(o能訪問Students的原型)
2.student執行,執行Person的代碼,this是o,並且傳入name, o.name='小明'返回的k是undefined
3.返回o,也就是返回{name:'小明'}

今天先寫到這,未完待續....

es6繼承

in

mvvm中definePrototype

對象的三種角色

靜態屬性、方法。私有屬性、方法

綜合訓練

總結

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章