JS繼承,中間到底幹了些什麼

2021-09-24 06:49:55 字數 4385 閱讀 1379

1.實現new函式

在js中初始化乙個例項的時候,會呼叫new去完成例項化,那麼new函式到底幹了些什麼事情,

此外,我們都知道在chrome,firefox等瀏覽器中,例項化的物件一般都通過 __proto__指向建構函式的原型,所以會有一條如下的對應關係

function

person () {}

var p = new person()

p.__proto__ = person.prototype

複製**

所以我們可以實現最簡單的new的山寨函式

function

mocknew()

複製**

通過以上方法,我們就山寨了乙個最簡單的new方法,這個山寨的new方法可以更好的幫我們去理解繼承的全部過程。

2.原形鏈繼承過程以及缺點解析

首先我們都知道原形鏈繼承會存在一定的問題,紅寶書上說的很清楚,這種繼承方式會產生兩個問題

第乙個原因很清楚也很容易解決,那麼為什麼會專門對引用型別產生問題呢,還是先上**

//父類animal

function

animal()

animal.prototype = ,

//更改food

givefood: function(food)

}//子類animalchild

function

animalchild() {}

//繫結原形鏈

animalchild.prototype = new animal()

let cat = new animalchild()

let dog = new animalchild()

cat.setname('cat')

cat.givefood('fish')

console.log(cat.name) //cat 輸出cat很正常

console.log(dog.name) //animal 這個就有點神奇了

console.log(cat.food) // ['food','fish']

console.log(dog.food) // ['food','fish']

複製**

通過以上的例子,我們發現原形式繼承並不是所有的資料都會共享,產生影響的資料只有引用型別的,這個的原因是為什麼呢,我們來使用我們的山寨new方法回顧整個過程

//例項話父類,繫結到子類的原形鏈上

animalchild.prototype = mocknew(animal)

複製**

當我們這麼呼叫的時候,回顧一下mocknew的執行過程,其中會執行這樣一步

//在構造的過程中,子類會呼叫父類建構函式

複製**

這步的執行,會導致本身在父類建構函式中的this.name被繫結到了乙個新的函式上,因為最終的返回值被複製到子型別的protoype上,所以,子類的protoye長得是以下模樣

//列印子類的prototype

console.log(animalchild.prototype)

//列印結果

animalchild.prototype = ,

givefood: function

() {}

}}複製**

可以看出藉由new方法,父類建構函式中的變數綁在在子類的原形上(prototype),而父類的原形綁在了子類原形的原形例項上(prototype.proto)

緊接著我們在例項化子型別例項

var cat = mocknew(animaltype)

複製**

這步我們會修改cat物件的__proto__屬性,最終生成的cat例項列印如下

console.log(cat)

//列印的結果

cat = ,

givefood: function() {}}}

}複製**

可以看出所有父型別的變數都被繫結在了例項的原形上,那為什麼引用型別的資料型別會產生錯誤呢,這其實和引用型別的修改方式有關

當我們修改name的時候,函式會主動在物件本身去賦值,及

cat.name = 'cat'

console.log(cat)

//列印結果

cat =

}複製**

而當我們對引用型別的陣列進行操作的時候,函式會優先找函式本身時候有這個變數,如果沒有的話,回去原形鏈上找

cat.name.push('fish')

console.log(cat)

//列印結果

cat =

}複製**

3.寄生組合式繼承

雖然說原形式繼承會帶來問題,但是實現的思路是非常有用的,對於父類的方法,變數,統統放在原形鏈上,繼承的時候,將同名的內容統統覆蓋,放在物件本身,這樣就解決了函式的繼承和內容的重寫

基於此,寄生組合的方法得到重視,下面分析以下執行過程,依然是使用上面的animal和animalchild類

//寄生組合式方法呼叫

//宣告父類

function

animal

() animal.prototype = ,

//更改food

givefood: function(food)

}//開始繼承

function

animalchild

() function

f() {}

f.prototype = animal.prototype

var prototype = new f()

prototype.constructor = animalchild

animalchild.prototype = prototype

複製**

上述**和原形式繼承主要有兩點區別

首先說一下第一點,呼叫call,呼叫call之後,相當於在子類的建構函式內部執行了一變父類的建構函式,這個時候,父函式內部通過this宣告得一些屬性都轉嫁到了子函式的建構函式中,這樣就解決了原形式繼承中變數共享的問題

其次,下面的prototype賦值方法帶有一點優化的屬性,因為父類建構函式中的內容通過call已經全部拿到了,只需要再將原形繫結就可以了,此外,通過new的方式,子類的掛在原形鏈上的方法實際上是和父類原形方法跨層級的

//為子類新增原形方法

animalchild.prototype.childmethod = function() {}

console.log(animalchild.prototype)

//列印結果

animalchild.prototype = ,

//父類的原形方法都在這裡

__proto__: ,

givefood: function() {}

}}複製**

es6中的super

通過寄生組合式繼承我們可以得到如下結論,加入b繼承了a,那麼可以得到乙個等式

b.prototype.__proto__ = a.prototype

複製**

滿足這個等式的話其實我們就可以說b繼承了a的原形鏈結

在es6中的super效果下,其實實現了兩條等式

b.__proto__ = a

//原形鏈相等

b.prototype.__proto__ = a.prototype

複製**

第二條等式我們理解,那麼第一條等式是什麼意思呢,在寄生組合式繼承中,我們使用call的方式去呼叫父類建構函式,而在es6中,我們可以理解為子類的建構函式是基於父類例項的加工,super返回的是乙個父類的例項,這樣也就解釋了等式一之間的關係。

當然,es6中的實現方法更為優雅,藉由乙個es6中提供的api:setprototypeof,可以用如下方式實現

class a {}

class b {}

//原形繼承

object.setprototypeof(b.prototype, a.prototype);

//建構函式繼承

object.setprototypeof(b, a);

複製**

結語

仔細的總結了以下之後,發現對於js更了解了!

js中new操作符到底幹了什麼?

先看乙個例子 function person person.prototype.test function let p1 newperson console.log p1.name andy console.log p1.age 20 p1.say hello everybody p1.test t...

js中的new 到底做了些什麼??

new在執行時會做四件事情 new會在記憶體中建立乙個新的空物件 new 會讓this指向這個新的物件 執行建構函式 目的 給這個新物件加屬性和方法 new會返回這個新物件基於上面的講解,我們執行如下 var obj new base new操作符具體幹了什麼呢?其實很簡單,就幹了三件事情。var ...

輸入npm i 後 npm幹了些什麼

語義化版本1 更新機制 1.2.3版本相容 會更新到1.2.3到2.0.0之間的版本,不包含2.0.0 1.2.3大致相當於同乙個版本 會更新到1.2.3到1.3.0之間的版本,不包含1.3.0npm i輸入後的包更新順序 從專案package.lock.json中檢視依賴版本的integrity屬...