成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

ES6 class繼承與super關(guān)鍵詞深入探索

jubincn / 3181人閱讀

摘要:請(qǐng)看對(duì)應(yīng)版本干了什么可知,相當(dāng)于以前在構(gòu)造函數(shù)里的行為。這種寫法會(huì)與上文中寫法有何區(qū)別我們?cè)诃h(huán)境下運(yùn)行一下,看看這兩種構(gòu)造函數(shù)的有何區(qū)別打印結(jié)果打印結(jié)果結(jié)合上文中關(guān)于原型的論述,仔細(xì)品味這兩者的差別,最好手動(dòng)嘗試一下。

ES6 class

在ES6版本之前,JavaScript語言并沒有傳統(tǒng)面向?qū)ο笳Z言的class寫法,ES6發(fā)布之后,Babel迅速跟進(jìn),廣大開發(fā)者也很快喜歡上ES6帶來的新的編程體驗(yàn)。
當(dāng)然,在這門“混亂”而又精妙的語言中,許多每天出現(xiàn)我們視野中的東西卻常常被我們忽略。
對(duì)于ES6語法,考慮到瀏覽器的兼容性問題,我們還是要把代碼轉(zhuǎn)換為ES5版本運(yùn)行。然而,之前的ES版本為什么能模仿ES6的諸多特性,比如class與繼承,super,static?JavaScript又做了哪些改變以應(yīng)對(duì)這些新角色?本文將對(duì)class實(shí)例構(gòu)造,class繼承關(guān)系,super關(guān)鍵字,static關(guān)鍵字的運(yùn)行機(jī)制進(jìn)行探索。
水平有限,文中若有引起困惑或錯(cuò)誤之處,還望指出。

class實(shí)例構(gòu)造 class基本樣例

基本而言,ES6 class形式如下:

 class Whatever{
      
 }

當(dāng)然,還可以有constructor方法。

class Whatever{
          constructor(){
             this.name = "hahaha";
         } 
 }

請(qǐng)看ES5對(duì)應(yīng)版本:

function Whatever{
    this.name = "hahaha";
}
new干了什么

可知,constructor相當(dāng)于以前在構(gòu)造函數(shù)里的行為。而對(duì)于ES5構(gòu)造函數(shù)而言,在被new調(diào)用的時(shí)候,大體上進(jìn)行了下面四步:

新建對(duì)象var _this = {};

this的[[prototype]]指向構(gòu)造函數(shù)的prototype,即_this.__proto_ = Constructor.prototype

改變Constructor的this到_this并執(zhí)行Constructor,即Constructor.apply(_this,agrs);得到構(gòu)造好的_this對(duì)象

判斷Constructor的返回值,若返回值不為引用類型,則返回_this,否則返回改引用對(duì)象

所以,構(gòu)造函數(shù)的實(shí)例會(huì)繼承掛載在prototype上的方法,在ES6 calss中,我們這樣寫會(huì)把方法掛載在class的prototype:

class Whatever{
    //...
    methodA(){
        //...
    }          
}

對(duì)應(yīng)ES5寫法:

Whatever.prototype = function methodA(){
            //...
        }

class繼承關(guān)系 原型語言基本特點(diǎn)

在基于原型的語言,有以下四個(gè)特點(diǎn):

一切皆為對(duì)象(js中除了對(duì)象還有基本類型,函數(shù)式第一等對(duì)象)

對(duì)象皆是從其他對(duì)象復(fù)制而來(在JS對(duì)象世界中,萬物始于Object.prototype這顆蛋)

對(duì)象會(huì)記住它的原型(在JS中對(duì)象的__proto__屬性指向它的原型)

調(diào)用對(duì)象本身沒有的屬性/方法時(shí),對(duì)象會(huì)嘗試委托它的原型

看到這,大家應(yīng)該明白了,為什么掛載在Constructor.prototype的方法會(huì)被實(shí)例“繼承”!
在ES6 class中,繼承關(guān)系還是由[[prototype]]連維持,即:

Child.prototype.__proto__ === Parent.prototype;
Child.__proto__ === Parent;
childObject.__proto === Child.prototype;
當(dāng)箭頭函數(shù)與class碰撞

ES6的箭頭函數(shù),一出身便深受眾人喜愛,因?yàn)樗鉀Q了令人頭疼的函數(shù)執(zhí)行時(shí)動(dòng)態(tài)this指向的“問題”(為什么加引號(hào)?因?yàn)橛袝r(shí)候我們有時(shí)確實(shí)需要?jiǎng)討B(tài)this帶來的巨大便利)。箭頭函數(shù)中this綁定在詞法作用域,即它定義的地方:

//ES6:
const funcArrow = () => {
    //your code
}
//ES5:
var _this = this;
var funcArrow = function(){
    this = _this;
    //your code
}

有的童鞋可能會(huì)想到了,既然js中繼承和this的關(guān)系這么大,在calss中采用詞法綁定this的箭頭函數(shù),會(huì)有怎么樣呢?
我們來瞧瞧。

class WhateverArrow{
        //
        methodArrow = () => {
            //...
        }          
    }

這種寫法會(huì)與上文中寫法有何區(qū)別?

class WhateverNormal{
        //
        methodNormal() {
            //...
        }          
    }
    

我們?cè)赾hrome環(huán)境下運(yùn)行一下,看看這兩種構(gòu)造函數(shù)的prototype有何區(qū)別:

WhateverArrow.prototype打印結(jié)果:
constructor: class Whatever1
__proto__: Object
WhateverNormal.prototype打印結(jié)果:
constructor: class Whatever2
methodNormal: ? methodNormal()
__proto__: Object

結(jié)合上文中關(guān)于原型的論述,仔細(xì)品味這兩者的差別,最好手動(dòng)嘗試一下。

方法與函數(shù)類型屬性

我們稱func(){}的形式為“方法”,而methodArrow = () =>:any為屬性!方法會(huì)被掛載在prototype,在屬性不會(huì)。箭頭函數(shù)methodArrow屬性會(huì)在構(gòu)造函數(shù)里賦值給this:

this.methodArrow = function methodArrow(){
    this = _this;
    //any code
}

在實(shí)例調(diào)用methodArrow時(shí),調(diào)用的是自己的methodArrow,而非委托calss WhateverArrow.prototype上的方法,而這個(gè)箭頭函數(shù)中this的指向,Babel或許能給我們一些啟示:

 var WhateverArrow = function WhateverArrow() {
  var _this = this;

  _classCallCheck(this, WhateverArrow);

  _defineProperty(this, "methodArrow", function () {
    consoe.log(_this);
  });
};
遇見extends,super與[[HomeObject]] 讓我們extends一下

當(dāng)我們談?wù)摾^承時(shí),往往指兩種:

對(duì)象實(shí)例繼承自一個(gè)類(構(gòu)造函數(shù))

子類繼承父類

上文中我們探討了第一種,現(xiàn)在,請(qǐng)把注意力轉(zhuǎn)向第二種。
考慮下方代碼:

class Parent {
    constructor(){
        this.tag = "A";
        this.name = "parent name"
    }
    methodA(){
        console.log("methodA in Parent")
    }
    methodB(){
        console.log(this.name);
    }
}

class Child extends Parent{
    constructor(){
        super();        
        //調(diào)用super()之后才用引用this
        this.name = "child name"
    }
    methodA(){
        super.methodA();
        console.log("methodA in Child")
    }
}
const c1 = new Child();
c1.methodA();//methodA in Parent // methodA in Child

我們通過extends連接了兩個(gè)class,標(biāo)明他們是“父子關(guān)系”的類,子類中方法會(huì)屏蔽掉父類中同名方法,與Java中多態(tài)特性不同,這里的方法參數(shù)數(shù)量并不影響“是否同一種方法”的判定。
在Child的constructor中,必須在調(diào)用super()之后才能調(diào)用this,否則將會(huì)因this為undefined而報(bào)錯(cuò)。其中緣由,簡(jiǎn)單來說就是執(zhí)行new操作時(shí),Child的_this來自于調(diào)用Parent的constructor,若不調(diào)用super(),_this將為undefined。對(duì)這個(gè)問題感興趣的同學(xué)可以自行操作試試,并結(jié)合Babel的轉(zhuǎn)換結(jié)果,進(jìn)行思考。

super來自何方?[[HomeObject]]為何物? super干了什么

super可以讓我們?cè)谧宇愔薪栌酶割惖膶傩院头椒ā?/p>

 methodA(){
            super.methodA();
            console.log("methodA in Child")
        }
        

super關(guān)鍵詞真是一個(gè)增進(jìn)父子情的天才創(chuàng)意!
值得注意的是,子類中methodA調(diào)用super.methodA()時(shí)候,super.methodA中的this綁定到了子類實(shí)例。

super來自何方?如何請(qǐng)到super這位大仙?

用的舒服之后,我們有必要想一想,Child.prototype.methodA中的super是如何找到Parent.prototype.methodA的?
我們知道:

Child.prototype.__proto__ === Parent.prototype;
cs.__proto__ === Child.prototype;
c1.methodA();

當(dāng)c1.methodA()執(zhí)行時(shí),methodA中this指向c1,難道通過多少人愛就有多少人恨的this?
仔細(xì)想想,如果是這樣(通過this找),考慮如下代碼:

//以下代碼刪除了當(dāng)前話題無關(guān)行
class GrandFather{
    methodA(){            
        console.log("methodA in GrandFather")
    }  
}
class Parent extends GrandFather{       
    methodA(){
        super.methodA();
        console.log("methodA in Parent")
    }       
}
    
class Child extends Parent{
   methodA(){
        super.methodA();
        console.log("methodA in Child")
    }
}

想想我們現(xiàn)在是執(zhí)行引擎,我們通過this找到了c1,然后通過原型找到了Child.prototype.methodA;
在Child.prototype.methodA中我們遇見了super.methodA();
現(xiàn)在我們要去找super,即Parent。
我們通過this.__proto__.__proto__methodA找到了Parent.prototype.methodA;
對(duì)于Parent.prototype.methodA來說,也要像對(duì)待c1一樣走這個(gè)方式找,即在Parent..prototype.methodA中通過this找其原型。
這時(shí)候問題來了,運(yùn)行到Parent.prototype.methodA時(shí),該方法中的this指向的還是c1。
這豈不是死循環(huán)了?
顯然,想通過this找super,只會(huì)鬼打墻。

[[HomeObject]]橫空出世

為了應(yīng)對(duì)super,js引擎干脆就讓方法(注意,是方法,不是屬性)在創(chuàng)建時(shí)硬綁定上[[HomeObject]]屬性,指向它所屬的對(duì)象!
顯然,Child中methodA的[[HomeObject]]綁定了Child.prototype,Parent中methodA的[[HomeObject]]綁定了Parent.prototype。
這時(shí)候,根據(jù)[[HomeObject]],可以準(zhǔn)確無誤地找到super!
而在Babel轉(zhuǎn)為ES5時(shí),是通過硬編碼的形式,解決了對(duì)super的引用,思路也一樣,硬綁定當(dāng)前方法所屬對(duì)象(對(duì)象或者函數(shù)):

//babel轉(zhuǎn)碼ES5節(jié)選
_createClass(Parent, [{
    key: "methodA",
    value: function methodA() {
        //此處就是對(duì)super.methodA()所做的轉(zhuǎn)換,同樣是硬綁定思路
      _get(_getPrototypeOf(Parent.prototype), "methodA", this).call(this);    
      console.log("methodA in Parent");
    }
  }]);
  

注意屬性與方法的差別:

var obj1 = {
    __proto__:SomePrototype,
    methodQ(){ //methodQ綁定了[[HomeObject]]->obj1,調(diào)用super
        super.someMethod();
    }
}

var obj2 = {
    __proto__:SomePrototype,
    methodQ:function(){ //methodQ不綁定任何[[HomeObject]]
        super.someMethod();//Syntax Eroor!語法錯(cuò)誤,super不允許在對(duì)象的非方法中調(diào)用
    }
}
箭頭函數(shù)再襲super

結(jié)合前文中關(guān)于class內(nèi)部箭頭函數(shù)的談?wù)?,有個(gè)問題不得不引起我們思考:class中的箭頭函數(shù)里的super指向哪里?
考慮如下代碼:

class Parent{       
    methodA(){       
       console.log("methodA in Parent")
    }       
}
    
class Child extends Parent{
    methodA = () => {
       super.methodA();
       console.log("methodA in Child")
    }
}

const c1 = new Child();
c1.methodA();

輸出為:

methodA in Parent
methodA in Child

似乎沒什么意外。我們需要更新異步,把Parent的methodA方法改為箭頭函數(shù):

class Parent{       
    methodA = () => {       
       console.log("methodA in Parent")
    }       
}
    
class Child extends Parent{
    methodA = () => {
       super.methodA();
       console.log("methodA in Child")
    }
}

const c1 = new Child();
c1.methodA();

很抱歉,人見人恨得異常發(fā)生了:

Uncaught TypeError: (intermediate value).methodA is not a function
    at Child.methodA 
    

如何把Child中的methodA改為普通方法函數(shù)呢?

class Parent{       
    methodA = () => {       
       console.log("methodA in Parent")
    }       
}
    
class Child extends Parent{
    methodA () {
       super.methodA();
       console.log("methodA in Child")
    }
}

const c1 = new Child();
c1.methodA();

輸出:

methodA in Parent
//并沒有打印methodA in Child

以上幾種結(jié)果產(chǎn)生的原因請(qǐng)結(jié)合前幾章節(jié)細(xì)致品味,你會(huì)有所收獲的。

不容忽視的static static的表現(xiàn)

簡(jiǎn)單來說,static關(guān)鍵詞標(biāo)志了一個(gè)掛載在class本身的屬性或方法,我們可以通過ClassName.staticMethod訪問到。

class Child{
    static name = "7788";    
    static methodA () {       
       console.log("static methodA in Child")
    }
}
Child.name;//7788;
Child.methodA();//static methodA in Child
static如何傳給子類

因?yàn)镃hild本身的[[prototype]]指向了Parent,即Child.__proto__===Parent 所以,static可以被子類繼承:

class Parent{       
    static methodA () {       
       console.log("static methodA in Parent")
    }     
}
    
class Child extends Parent{
    
}

Child.methodA();//static methodA in Parent

static方法中訪問super
class Parent{       
    static methodA () {       
       console.log("static methodA in Parent")
    }     
}
    
class Child extends Parent{
    static methodA () {   
        super.methodA()    
       console.log("static methodA in Child")
    }  
}

Child.methodA();
//輸出:
//static methodA in Parent
// static methodA in Child


結(jié)語

JS是門神奇的語言,神奇到很多人往往會(huì)用JS但是不會(huì)JS(...hh)。作為一門熱門且不斷改進(jìn)中的語言,由于跟隨時(shí)代和歷史遺留等方面的因素,它有很多令人迷惑的地方。
在我們每天面對(duì)的一些特性中,我們很容易忽視其中機(jī)理。就算哪天覺得自己明白了,過一段時(shí)間可能又遇到別的問題,突然覺得自己懂得還是太少(還是太年輕)。然后刨根問底的搞明白,過一段時(shí)間可能又。。?;蛘哐芯縅S的歷程就是這樣螺旋式的進(jìn)步吧。
感謝Babel,她真的對(duì)我們理解JS一些特性的運(yùn)行機(jī)理非常有用,因?yàn)锽abel對(duì)JS吃的真的很透徹(...)。她對(duì)ES6的“翻譯”,可以幫助我們對(duì)ES6新特性以及往前版本的JS的理解。
行文匆忙,難免有錯(cuò)漏之處,歡迎指出。
祝大家身體健康,BUG越來越少。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/106902.html

相關(guān)文章

  • 從-1開始的ES6探索之旅02:小伙子,你對(duì)象咋來的?續(xù)篇 - 你的對(duì)象班(class)里來的?

    摘要:這是因?yàn)樽宇悰]有自己的對(duì)象,而是繼承父類的對(duì)象,然后對(duì)其進(jìn)行加工。 溫馨提示:作者的爬坑記錄,對(duì)你等大神完全沒有價(jià)值,別在我這浪費(fèi)生命溫馨提示-續(xù):你們要非得看,我也攔不住,但是至少得準(zhǔn)備個(gè)支持ES6的Chrome瀏覽器吧?溫馨提示-再續(xù):ES6簡(jiǎn)直了,放著不用簡(jiǎn)直令人發(fā)指! 書接上回,即便是程序員,也還是能夠通過自己的努力辛辛苦苦找到合適對(duì)象的,見前文《javascript對(duì)象不完全...

    incredible 評(píng)論0 收藏0
  • JavaScript是如何工作的:深入類和繼承內(nèi)部原理+Babel和 TypeScript 之間轉(zhuǎn)換

    摘要:下面是用實(shí)現(xiàn)轉(zhuǎn)成抽象語法樹如下還支持繼承以下是轉(zhuǎn)換結(jié)果最終的結(jié)果還是代碼,其中包含庫(kù)中的一些函數(shù)。可以使用新的易于使用的類定義,但是它仍然會(huì)創(chuàng)建構(gòu)造函數(shù)和分配原型。 這是專門探索 JavaScript 及其所構(gòu)建的組件的系列文章的第 15 篇。 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! 如果你錯(cuò)過了前面的章節(jié),可以在這里找到它們: JavaScript 是...

    PrototypeZ 評(píng)論0 收藏0
  • 探索 proto & prototype 繼承之間的關(guān)系

    摘要:而和的存在就是為了建立這種子類與父類間的聯(lián)系。創(chuàng)建一個(gè)基本對(duì)象建立新對(duì)象與原型我把它理解為類之間的連接執(zhí)行構(gòu)造函數(shù)小結(jié)可以理解為類,也就是存儲(chǔ)一類事物的基本信息。原型原型鏈和繼承之間的關(guān)系。 原型 原型的背景 首先,你應(yīng)該知道javascript是一門面向?qū)ο笳Z言。 是對(duì)象,就具有繼承性。 繼承性,就是子類自動(dòng)共享父類的數(shù)據(jù)結(jié)構(gòu)和方法機(jī)制。 而prototype 和 __proto__...

    dockerclub 評(píng)論0 收藏0
  • ES6深入淺出 Classes

    摘要:一步,一步前進(jìn)一步深入淺出之。是構(gòu)造函數(shù),可在里面初始化我們想初始化的東西。類靜態(tài)方法大多數(shù)情況下,類是有靜態(tài)方法的。中添加類方法十分容易類方法和靜態(tài)方法是同一個(gè)東西在的語法中,我們可以使用關(guān)鍵字修飾方法,進(jìn)而得到靜態(tài)方法。 一步,一步前進(jìn)の一步 ES6深入淺出之Classes。翻譯的同時(shí)亂加個(gè)人見解,強(qiáng)烈推薦閱讀原作者的文章,言簡(jiǎn)意賅。es6-classes-in-depth 類語...

    array_huang 評(píng)論0 收藏0
  • 使用ES6寫更好的JavaScript

    摘要:但在可以用和的地方使用它們很有好處的。它會(huì)盡可能的約束變量的作用域,有助于減少令人迷惑的命名沖突。在回調(diào)函數(shù)外面,也就是中,它指向了對(duì)象。這就意味著當(dāng)引擎查找的值時(shí),可以找到值,但卻和回調(diào)函數(shù)之外的不是同一個(gè)值。 使用 ES6 寫更好的 JavaScript part I:廣受歡迎新特性 介紹 在ES2015規(guī)范敲定并且Node.js增添了大量的函數(shù)式子集的背景下,我們終于可以拍著胸脯...

    Dionysus_go 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<