成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

ES6 class繼承與super關鍵詞深入探索

jubincn / 3123人閱讀

摘要:請看對應版本干了什么可知,相當于以前在構造函數(shù)里的行為。這種寫法會與上文中寫法有何區(qū)別我們在環(huán)境下運行一下,看看這兩種構造函數(shù)的有何區(qū)別打印結果打印結果結合上文中關于原型的論述,仔細品味這兩者的差別,最好手動嘗試一下。

ES6 class

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

class實例構造 class基本樣例

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

 class Whatever{
      
 }

當然,還可以有constructor方法。

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

請看ES5對應版本:

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

可知,constructor相當于以前在構造函數(shù)里的行為。而對于ES5構造函數(shù)而言,在被new調用的時候,大體上進行了下面四步:

新建對象var _this = {};

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

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

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

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

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

對應ES5寫法:

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

class繼承關系 原型語言基本特點

在基于原型的語言,有以下四個特點:

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

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

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

調用對象本身沒有的屬性/方法時,對象會嘗試委托它的原型

看到這,大家應該明白了,為什么掛載在Constructor.prototype的方法會被實例“繼承”!
在ES6 class中,繼承關系還是由[[prototype]]連維持,即:

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

ES6的箭頭函數(shù),一出身便深受眾人喜愛,因為它解決了令人頭疼的函數(shù)執(zhí)行時動態(tài)this指向的“問題”(為什么加引號?因為有時候我們有時確實需要動態(tài)this帶來的巨大便利)。箭頭函數(shù)中this綁定在詞法作用域,即它定義的地方:

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

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

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

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

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

我們在chrome環(huán)境下運行一下,看看這兩種構造函數(shù)的prototype有何區(qū)別:

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

結合上文中關于原型的論述,仔細品味這兩者的差別,最好手動嘗試一下。

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

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

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

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

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

  _classCallCheck(this, WhateverArrow);

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

當我們談論繼承時,往往指兩種:

對象實例繼承自一個類(構造函數(shù))

子類繼承父類

上文中我們探討了第一種,現(xià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();        
        //調用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連接了兩個class,標明他們是“父子關系”的類,子類中方法會屏蔽掉父類中同名方法,與Java中多態(tài)特性不同,這里的方法參數(shù)數(shù)量并不影響“是否同一種方法”的判定。
在Child的constructor中,必須在調用super()之后才能調用this,否則將會因this為undefined而報錯。其中緣由,簡單來說就是執(zhí)行new操作時,Child的_this來自于調用Parent的constructor,若不調用super(),_this將為undefined。對這個問題感興趣的同學可以自行操作試試,并結合Babel的轉換結果,進行思考。

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

super可以讓我們在子類中借用父類的屬性和方法。

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

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

super來自何方?如何請到super這位大仙?

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

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

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

//以下代碼刪除了當前話題無關行
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;
對于Parent.prototype.methodA來說,也要像對待c1一樣走這個方式找,即在Parent..prototype.methodA中通過this找其原型。
這時候問題來了,運行到Parent.prototype.methodA時,該方法中的this指向的還是c1。
這豈不是死循環(huán)了?
顯然,想通過this找super,只會鬼打墻。

[[HomeObject]]橫空出世

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

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

注意屬性與方法的差別:

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

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

結合前文中關于class內部箭頭函數(shù)的談論,有個問題不得不引起我們思考: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é)細致品味,你會有所收獲的。

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

簡單來說,static關鍵詞標志了一個掛載在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如何傳給子類

因為Child本身的[[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


結語

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

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

轉載請注明本文地址:http://systransis.cn/yun/106902.html

相關文章

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

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

    incredible 評論0 收藏0
  • JavaScript是如何工作的:深入類和繼承內部原理+Babel和 TypeScript 之間轉換

    摘要:下面是用實現(xiàn)轉成抽象語法樹如下還支持繼承以下是轉換結果最終的結果還是代碼,其中包含庫中的一些函數(shù)??梢允褂眯碌囊子谑褂玫念惗x,但是它仍然會創(chuàng)建構造函數(shù)和分配原型。 這是專門探索 JavaScript 及其所構建的組件的系列文章的第 15 篇。 想閱讀更多優(yōu)質文章請猛戳GitHub博客,一年百來篇優(yōu)質文章等著你! 如果你錯過了前面的章節(jié),可以在這里找到它們: JavaScript 是...

    PrototypeZ 評論0 收藏0
  • 探索 proto & prototype 繼承之間的關系

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

    dockerclub 評論0 收藏0
  • ES6深入淺出 Classes

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

    array_huang 評論0 收藏0
  • 使用ES6寫更好的JavaScript

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

    Dionysus_go 評論0 收藏0

發(fā)表評論

0條評論

jubincn

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<