日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

从babel实现es6类的继承来深入理解js的原型及继承

發(fā)布時間:2025/3/20 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从babel实现es6类的继承来深入理解js的原型及继承 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

先聊個5毛錢的背景吧

自從有了babel這一個利器之后,es6現(xiàn)在已經(jīng)被廣泛的使用。JavaScript 類實質上是 JavaScript 現(xiàn)有的基于原型的繼承的語法糖。類語法不會為JavaScript引入新的面向對象的繼承模型。也就是說babel無論是實現(xiàn)類,還是實現(xiàn)繼承,本質上都是基于原型及原型鏈的,那我我們就順這這個思路,一步一步往下走,直到揭開babel是如何實現(xiàn)類及類的繼承的。

原型和原型鏈

javascript中原型的概念比較抽象,不是很好理解,我們還是老老實實上代碼,用代碼來舉例說明。

function Person (name, age, job) {this.name = name;this.age = age;this.job = job; } let p1 = new Person('張三', 18, '前端攻城獅') 復制代碼

我們創(chuàng)建了一個Person的構造函數(shù),并用new 創(chuàng)建了一個該對象的實例對象p1。

  • 在js中,每一個函數(shù)都有一個prototype屬性。
  • 每一個js對象(除null)外都有一個__proto__的屬性。 那么問題來啦,函數(shù)Person的prototype的屬性指向的是什么呢?是不是Person的原型呢?我們在chrome中輸入如下代碼
    我們發(fā)現(xiàn)Person.prototype指向一個對象,實際上這個對象就是p1的原型。如圖: 因此,我們也可以用下面的這個圖來表示他們之間的關系
  • 前面我們說過,Person.prototype指向一個對象,那么這個對象中都有什么呢?我們再在chrome中輸入如下代碼如圖: 我們發(fā)現(xiàn)Person.prototype的確是一個對象,這個對象里面有兩個屬性:constrcutor, 和__proto__這兩個屬性。constructor指向Person(),也說明js對象果真都有一個__proto__的屬性啊。那問題來啦constructor到底和Person是什么關系呢?實際上,constructor屬性指向的就是構造函數(shù)本身。有圖有真相

我們再來總結一下吧:

  • 每個原型上都有一個constructor的屬性指向關聯(lián)的構造函數(shù)。他們的關系如下: 前面我們發(fā)現(xiàn)Person.prototype也有__proto__屬性,那Person.prototype.__proto__是什么呢?換言之,原型的原型是什么呢。先來一張圖 我們發(fā)現(xiàn)它只有constructor屬性,該屬性指向Object(),沒有__proto__屬性。是不是很有意思。那我們再做個實驗吧。如圖

通過這張圖我們發(fā)現(xiàn)

  • 原型對象就是通過Object構造函數(shù)生成的,實例的__proto__指向構造函數(shù)的prototype,Object構造函數(shù)的原型的__proto__指向null
  • 我們用一張圖總結一下吧: 實際上這個就是原型鏈啦。

ES5中的繼承

前面我們聊了聊原型和原型鏈,其實就是為了我們聊繼承做鋪墊的。廢話不多說,我們上代碼吧:

// 我們定義一個動物類,里面有一個類型的屬性 function Animal (type) {this.type = type } // 動物都有吃東西的方法 Animal.prototype.eat = function () {console.log('我在吃東西') } // 我們來個小貓咪類吧 function Cat () {} // 現(xiàn)在我們要實現(xiàn)讓Cat繼承Animal的屬性和方法,該怎么做呢? 復制代碼
  • 第一種方法
// 我們將cat類的原型直接指向Animal的實例 Cat.prototype = new Animal() let cat = new Cat() console.log(cat.eat()) 復制代碼

結果如圖

我們發(fā)現(xiàn)無法繼承父類的屬性。我們改造一下,借助call方法,代碼入下:

function Animal (type) {this.type = type } Animal.prototype.eat = function () {console.log('我在吃東西') } function Cat (type) {Animal.call(this, type) } Cat.prototype = new Animal() let cat = new Cat('喵咪') console.log(cat.eat()) console.log(cat.type) 復制代碼

運行結果如下:

nice!!!似乎完美的解決了問題,但真的是這樣么? 我就不賣關子啦,實際上是有問題的,有什么問題呢?我們上代碼:

function Animal (type, val) {this.type = typethis.sum = [4,5,6] // 隨便給的屬性啊,為了說明問題。this.setSum = function () {this.sum.push(val)} } Animal.prototype.eat = function () {console.log('我在吃東西') } function Cat (type, val) {Animal.call(this, type, val) } Cat.prototype = new Animal() let cat = new Cat('喵咪', 1) let cat2 = new Cat('貓咪2', 2) console.log(cat.setSum()) console.log(cat2.setSum()) console.log(cat.sum) console.log(cat2.sum) 復制代碼

運行結果如圖:

發(fā)現(xiàn)了沒有,圖中setSum方法和sum屬性都是父類定義的,但是子類可以調用。所以這個不是我們想要的結果;還有一點就是Cat的constructor屬性,此時指向的并不是Cat而是Animal。那應該怎么解決呢?

  • 第二種方法: 這個時候我們就不得不拿出我們的終極神器啦。它就是ES5的Object.create()方法。代碼如下:
function inherit(C, P) {// 等同于臨時構造函數(shù)C.prototype = Object.create(P.prototype);C.prototype.constructor = C; // 修復constructorC.super = P;//存儲超類 } function Animal(type) {this.type = type; } Animal.prototype.eat = function () {console.log('動物都是要吃飯滴') } function Cat(type, talk) {Cat.super.call(this, type)this.talk = talkthis.getTalk = function () {return this.talk} } inherit(Cat, Animal) let cat = new Cat('貓咪', '我們一起學貓叫') console.log(cat.getTalk()) 復制代碼

代碼運行如下:

這樣我們就比較完美的解決了繼承的問題。

babel對es6類的實現(xiàn)

在背景中我們已經(jīng)聊到了es6中類的繼承實際上是一個語法糖,現(xiàn)在我們就想辦法撥開這顆糖。

  • es6類重寫上面代碼
class Animal {constructor (type) {this.type = type}eat () {console.log('動物都要吃飯')} }class Cat extends Animal {constructor (type,talk) {super(type) // 繼承父類constructor的屬性this.talk = talk}getTalk () {return this.talk} } let cat = new Cat('喵咪', '喵喵喵') console.log(cat.getTalk()) // 喵喵喵 console.log(cat.type) // 喵咪 復制代碼
  • babel中類的實現(xiàn)
    babel中類的實現(xiàn)主要是三個步驟:
    • 步驟一: 構造函數(shù)的實現(xiàn),這部分的實現(xiàn)與我們普通的構造函數(shù)的區(qū)別就是,通過一個自執(zhí)行函數(shù)包裹,代碼如下 var Animal = function () {function Animal (type) {this.type = typethis.getType = function () {return this.type}}return Animal }()var Cat = function () {function Cat (talk) {this.talk = talkthis.getTalk = function () {return this.talk}}return Cat}() 復制代碼
    • 步驟二: 如下代碼: function Animal() {return {name: '牛逼',age: 18}}let animal = new Animal() console.log(animal) // { name: '牛逼', age: 18} 復制代碼因此babel中對此做了校驗,這就是第二步要干的事情。代碼如下: var _classCallCheck = function (instance, Constructor) {if (!(instance instanceof Constructor)) {throw new TypeError("不能返回一個對象")}}// 嗯,你沒有看錯就是這么幾行代碼var Animal = function () {function Animal (type) {_classCallCheck(this, Animal)this.type = type}}()...此處省略喵咪類 復制代碼
    • 步驟三: 原型上方法的添加。前面兩步主要是對構造函數(shù)的實現(xiàn)及校驗,步驟三就是把原型上的方法添加到對應的類上。這里我們主要要聊聊babel是如何對 es6中類的方法的處理,也就是 class Animal {eat () {console.log('aaa')}sleep () {console.log('bbb')}} 復制代碼如上面代碼中的eat方法和sleep方法的處理。那怎么能把方法添加到一個對象上呢?沒錯,Object.defineProperty()這個方法就是用來干這件事情的,babel中也是用他來處理的。回到我們的話題,在babel中,它是通過_createClass這個函數(shù)來處理的。廢話不多說我們上代碼: /* _createClass()這個函數(shù)接受兩個參數(shù),一個是這個類的構造函數(shù),即給哪個類添加方法。第二個參數(shù)是一個數(shù)組對象,類似這樣嬸的[{key: 'eat', val: function eat () { console.log('aaa') }}] 是不是恍然大悟?那接下來我們就實現(xiàn)一下吧*/function definePropties (target, props) {for (var i = 0; i < props.length; i++) {var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ('value' in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor)}}var _createClass = function () {return function (Constructor, protoProps, staticProps) {// 添加原型上的方法if (protoProps) definePropties(Constructor.prototype, protoProps)// 添加靜態(tài)的方法if (staticProps) definePropties(Constructor, staticProps) return Constructor}}()_createClass(Animal,[{key: 'eat',value: function () {console.log('aaa')}}]) 復制代碼
    到這里babel實現(xiàn)類就基本上實現(xiàn)啦。上面代碼可以多看,多思考,還是挺收益的。

babel中繼承的實現(xiàn)

接下來,終于到了我們的大boss啦,上面我們聊了聊babel中是怎么處理es6中類的,這節(jié)我們就聊聊babel中是怎么處理es6中的繼承。

  • babel中繼承的實現(xiàn) babel中繼承的實現(xiàn)的思路基本跟我們前面聊的es5的繼承思路基本一致。下面我們來聊一聊。
function _inherits(subClass, superClass) {subClass.prototype = Object.create(superClass.prototype, {constructor: {value: subClass,enumerable: false,writable: true,configurable: true;}})if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 復制代碼

好啦,這個話題就聊到這里,歡迎大家拍磚啊。

總結

以上是生活随笔為你收集整理的从babel实现es6类的继承来深入理解js的原型及继承的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。