国产99久久精品_欧美日本韩国一区二区_激情小说综合网_欧美一级二级视频_午夜av电影_日本久久精品视频

最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
當前位置: 首頁 - 科技 - 知識百科 - 正文

ES6Class繼承與super的介紹

來源:懂視網 責編:小采 時間:2020-11-27 19:34:03
文檔

ES6Class繼承與super的介紹

ES6Class繼承與super的介紹:這篇文章主要介紹了關于ES6 Class 繼承與 super的介紹,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下Class 繼承與 superclass 可以 extends 自另一個 class。這是一個不錯的語法,技術上基于原型繼承。要繼承一個對象,需要在 {..}
推薦度:
導讀ES6Class繼承與super的介紹:這篇文章主要介紹了關于ES6 Class 繼承與 super的介紹,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下Class 繼承與 superclass 可以 extends 自另一個 class。這是一個不錯的語法,技術上基于原型繼承。要繼承一個對象,需要在 {..}
這篇文章主要介紹了關于ES6 Class 繼承與 super的介紹,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下

Class 繼承與 super

class 可以 extends 自另一個 class。這是一個不錯的語法,技術上基于原型繼承。

要繼承一個對象,需要在 {..} 前指定 extends 和父對象。

這個 Rabbit 繼承自 Animal

class Animal {

 constructor(name) {
 this.speed = 0;
 this.name = name;
 }

 run(speed) {
 this.speed += speed;
 alert(`${this.name} runs with speed ${this.speed}.`);
 }

 stop() {
 this.speed = 0;
 alert(`${this.name} stopped.`);
 }

}


// Inherit from Animal
class Rabbit extends Animal {
 hide() {
 alert(`${this.name} hides!`);
 }
}


let rabbit = new Rabbit("White Rabbit");

rabbit.run(5); // White Rabbit runs with speed 5.
rabbit.hide(); // White Rabbit hides!

如你所見,如你所想,extend 關鍵字實際上是在 Rabbit.prototype 添加 [Prototype]],引用到 Animal.prototype

719959942-5b41c4dcd690b_articlex[1].png

所以現在 rabbit 既可以訪問它自己的方法,也可以訪問 Animal 的方法。

extends 后可跟表達式

Class 語法的 `extends' 后接的不限于指定一個類,更可以是表達式。

例如一個生成父類的函數:

function f(phrase) {
 return class {
 sayHi() { alert(phrase) }
 }
}


class User extends f("Hello") {}


new User().sayHi(); // Hello

例子中,class User 繼承了 f('Hello')返回的結果。

對于高級編程模式,當我們使用的類是根據許多條件使用函數來生成時,這就很有用。

重寫一個方法

現在讓我們進入下一步,重寫一個方法。到目前為止,RabbitAnimal 繼承了 stop 方法,this.speed = 0

如果我們在 Rabbit 中指定了自己的 stop,那么會被優先使用:

class Rabbit extends Animal {
 stop() {
 // ...this will be used for rabbit.stop()
 }
}

......但通常我們不想完全替代父方法,而是在父方法的基礎上調整或擴展其功能。我們進行一些操作,讓它之前/之后或在過程中調用父方法。

Class 為此提供 super關鍵字。

  • 使用 super.method(...) 調用父方法。

  • 使用 super(...) 調用父構造函數(僅在 constructor 函數中)。

  • 例如,讓兔子在 stop 時自動隱藏:

    class Animal {
    
     constructor(name) {
     this.speed = 0;
     this.name = name;
     }
    
     run(speed) {
     this.speed += speed;
     alert(`${this.name} runs with speed ${this.speed}.`);
     }
    
     stop() {
     this.speed = 0;
     alert(`${this.name} stopped.`);
     }
    
    }
    
    class Rabbit extends Animal {
     hide() {
     alert(`${this.name} hides!`);
     }
    
    
     stop() {
     super.stop(); // call parent stop
     this.hide(); // and then hide
     }
    
    }
    
    let rabbit = new Rabbit("White Rabbit");
    
    rabbit.run(5); // White Rabbit runs with speed 5.
    rabbit.stop(); // White Rabbit stopped. White rabbit hides!

    現在,Rabbitstop 方法通過 super.stop() 調用父類的方法。

    箭頭函數無 super

    正如在 arrow-functions 一章中提到,箭頭函數沒有 super

    它會從外部函數中獲取 super。例如:

    class Rabbit extends Animal {
     stop() {
     setTimeout(() => super.stop(), 1000); // call parent stop after 1sec
     }
    }

    箭頭函數中的 superstop() 中的相同,所以它按預期工作。如果我們在這里用普通函數,便會報錯:

    // Unexpected super
    setTimeout(function() { super.stop() }, 1000);

    重寫構造函數

    對于構造函數來說,這有點棘手 tricky。

    直到現在,Rabbit 都沒有自己的 constructor
    Till now, Rabbit did not have its own constructor.

    根據規范,如果一個類擴展了另一個類并且沒有 constructor ,那么會自動生成如下 constructor

    class Rabbit extends Animal {
     // generated for extending classes without own constructors
    
     constructor(...args) {
     super(...args);
     }
    
    }

    我們可以看到,它調用了父 constructor 傳遞所有參數。如果我們不自己寫構造函數,就會發生這種情況。

    現在我們將一個自定義構造函數添加到 Rabbit 中。除了name,我們還會設置 earLength

    class Animal {
     constructor(name) {
     this.speed = 0;
     this.name = name;
     }
     // ...
    }
    
    class Rabbit extends Animal {
    
    
     constructor(name, earLength) {
     this.speed = 0;
     this.name = name;
     this.earLength = earLength;
     }
    
    
     // ...
    }
    
    
    // Doesn't work!
    let rabbit = new Rabbit("White Rabbit", 10); // Error: this is not defined.

    哎呦出錯了!現在我們不能生成兔子了,為什么呢?

    簡單來說:繼承類中的構造函數必須調用 super(...),(!)并且在使用 this 之前執行它。

    ...但為什么?這是什么情況?嗯...這個要求看起來確實奇怪。

    現在我們探討細節,讓你真正理解其中緣由 ——

    在JavaScript中,繼承了其他類的構造函數比較特殊。在繼承類中,相應的構造函數被標記為特殊的內部屬性 [[ConstructorKind]]:“derived”

    區別在于:

  • 當一個普通的構造函數運行時,它會創建一個空對象作為 this,然后繼續運行。

  • 但是當派生的構造函數運行時,與上面說的不同,它指望父構造函數來完成這項工作。

  • 所以如果我們正在構造我們自己的構造函數,那么我們必須調用 super,否則具有 this 的對象將不被創建,并報錯。

    對于 Rabbit 來說,我們需要在使用 this 之前調用 super(),如下所示:

    class Animal {
    
     constructor(name) {
     this.speed = 0;
     this.name = name;
     }
    
     // ...
    }
    
    class Rabbit extends Animal {
    
     constructor(name, earLength) {
    
     super(name);
    
     this.earLength = earLength;
     }
    
     // ...
    }
    
    
    // now fine
    let rabbit = new Rabbit("White Rabbit", 10);
    alert(rabbit.name); // White Rabbit
    alert(rabbit.earLength); // 10

    Super 的實現與 [[HomeObject]]

    讓我們再深入理解 super 的底層實現,我們會看到一些有趣的事情。

    首先要說的是,以我們迄今為止學到的知識來看,實現 super 是不可能的。

    那么思考一下,這是什么原理?當一個對象方法運行時,它將當前對象作為 this。如果我們調用 super.method(),那么如何檢索 method?很容易想到,我們需要從當前對象的原型中取出 method。從技術上講,我們(或JavaScript引擎)可以做到這一點嗎?

    也許我們可以從 this 的 [[Prototype]] 中獲得方法,就像 this .__ proto __.method 一樣?不幸的是,這是行不通的。

    讓我們試一試,簡單起見,我們不使用 class 了,直接使用普通對象。

    在這里,rabbit.eat() 調用父對象的 animal.eat() 方法:

    let animal = {
     name: "Animal",
     eat() {
     alert(`${this.name} eats.`);
     }
    };
    
    let rabbit = {
     __proto__: animal,
     name: "Rabbit",
     eat() {
    
     // that's how super.eat() could presumably work
     this.__proto__.eat.call(this); // (*)
    
     }
    };
    
    rabbit.eat(); // Rabbit eats.

    (*) 這一行,我們從原型(animal)中取出 eat,并以當前對象的上下文中調用它。請注意,.call(this) 在這里很重要,因為只寫 this .__ proto __.eat() 的話 eat 的調用對象將會是 animal,而不是當前對象。

    以上代碼的 alert 是正確的。

    但是現在讓我們再添加一個對象到原型鏈中,就要出事了:

    let animal = {
     name: "Animal",
     eat() {
     alert(`${this.name} eats.`);
     }
    };
    
    let rabbit = {
     __proto__: animal,
     eat() {
     // ...bounce around rabbit-style and call parent (animal) method
     this.__proto__.eat.call(this); // (*)
     }
    };
    
    let longEar = {
     __proto__: rabbit,
     eat() {
     // ...do something with long ears and call parent (rabbit) method
     this.__proto__.eat.call(this); // (**)
     }
    };
    
    
    longEar.eat(); // Error: Maximum call stack size exceeded

    噢,完蛋!調用 longEar.eat() 報錯了!

    這原因一眼可能看不透,但如果我們跟蹤 longEar.eat() 調用,大概就知道為什么了。在 (*)(**) 兩行中, this 的值是當前對象(longEar)。重點來了:所有方法都將當前對象作為 this,而不是原型或其他東西。

    因此,在兩行 (*)(**) 中,this.__ proto__ 的值都是 rabbit。他們都調用了 rabbit.eat,于是就這么無限循環下去。

    情況如圖:

    1204806494-5b41c4dccebef_articlex[1].png

    1.在 longEar.eat() 里面,(**) 行中調用了 rabbit.eat,并且this = longEar

    // inside longEar.eat() we have this = longEar
    this.__proto__.eat.call(this) // (**)
    // becomes
    longEar.__proto__.eat.call(this)
    // that is
    rabbit.eat.call(this);

    2.然后在rabbit.eat(*) 行中,我們希望傳到原型鏈的下一層,但是 this = longEar,所以 this .__ proto __.eat又是 rabbit.eat

    // inside rabbit.eat() we also have this = longEar
    this.__proto__.eat.call(this) // (*)
    // becomes
    longEar.__proto__.eat.call(this)
    // or (again)
    rabbit.eat.call(this);
    1. ...因此 rabbit.eat 在無盡循環調動,無法進入下一層。

    這個問題不能簡單使用 this 解決。

    [[HomeObject]]

    為了提供解決方案,JavaScript 為函數添加了一個特殊的內部屬性:[[HomeObject]]

    當函數被指定為類或對象方法時,其 [[HomeObject]] 屬性為該對象。

    這實際上違反了 unbind 函數的思想,因為方法記住了它們的對象。并且 [[HomeObject]] 不能被改變,所以這是永久 bind(綁定)。所以在 JavaScript 這是一個很大的變化。

    但是這種改變是安全的。 [[HomeObject]] 僅用于在 super 中獲取下一層原型。所以它不會破壞兼容性。

    讓我們來看看它是如何在 super 中運作的:

    let animal = {
     name: "Animal",
     eat() { // [[HomeObject]] == animal
     alert(`${this.name} eats.`);
     }
    };
    
    let rabbit = {
     __proto__: animal,
     name: "Rabbit",
     eat() { // [[HomeObject]] == rabbit
     super.eat();
     }
    };
    
    let longEar = {
     __proto__: rabbit,
     name: "Long Ear",
     eat() { // [[HomeObject]] == longEar
     super.eat();
     }
    };
    
    
    longEar.eat(); // Long Ear eats.

    每個方法都會在內部 [[HomeObject]] 屬性中記住它的對象。然后 super 使用它來解析原型。

    在類和普通對象中定義的方法中都定義了 [[HomeObject]],但是對于對象,必須使用:method() 而不是 "method: function()"

    在下面的例子中,使用非方法語法(non-method syntax)進行比較。這么做沒有設置 [[HomeObject]] 屬性,繼承也不起作用:

    let animal = {
     eat: function() { // should be the short syntax: eat() {...}
     // ...
     }
    };
    
    let rabbit = {
     __proto__: animal,
     eat: function() {
     super.eat();
     }
    };
    
    
    rabbit.eat(); // Error calling super (because there's no [[HomeObject]])

    靜態方法和繼承

    class 語法也支持靜態屬性的繼承。

    例如:

    class Animal {
    
     constructor(name, speed) {
     this.speed = speed;
     this.name = name;
     }
    
     run(speed = 0) {
     this.speed += speed;
     alert(`${this.name} runs with speed ${this.speed}.`);
     }
    
     static compare(animalA, animalB) {
     return animalA.speed - animalB.speed;
     }
    
    }
    
    // Inherit from Animal
    class Rabbit extends Animal {
     hide() {
     alert(`${this.name} hides!`);
     }
    }
    
    let rabbits = [
     new Rabbit("White Rabbit", 10),
     new Rabbit("Black Rabbit", 5)
    ];
    
    rabbits.sort(Rabbit.compare);
    
    rabbits[0].run(); // Black Rabbit runs with speed 5.

    現在我們可以調用 Rabbit.compare,假設繼承的 Animal.compare 將被調用。

    它是如何工作的?再次使用原型。正如你猜到的那樣,extends 同樣給 Rabbit 提供了引用到 Animal[Prototype]

    1418177155-5b41c4dcd62c3_articlex[1].png

    所以,Rabbit 函數現在繼承 Animal 函數。Animal 自帶引用到 Function.prototype[[Prototype]](因為它不 extend 其他類)。

    看看這里:

    class Animal {}
    class Rabbit extends Animal {}
    
    // for static propertites and methods
    alert(Rabbit.__proto__ === Animal); // true
    
    // and the next step is Function.prototype
    alert(Animal.__proto__ === Function.prototype); // true
    
    // that's in addition to the "normal" prototype chain for object methods
    alert(Rabbit.prototype.__proto__ === Animal.prototype);

    這樣 Rabbit 可以訪問 Animal 的所有靜態方法。

    在內置對象中沒有靜態繼承

    請注意,內置類沒有靜態 [[Prototype]] 引用。例如,Object 具有 Object.definePropertyObject.keys等方法,但 ArrayDate 不會繼承它們。

    DateObject 的結構:

    3237107697-5b41c4dcd967d_articlex[1].png

    DateObject 之間毫無關聯,他們獨立存在,不過 Date.prototype 繼承于 Object.prototype,僅此而已。

    造成這個情況是因為 JavaScript 在設計初期沒有考慮使用 class 語法和繼承靜態方法。

    原生拓展

    Array,Map 等內置類也可以擴展。

    舉個例子,PowerArray 繼承自原生 Array

    // add one more method to it (can do more)
    class PowerArray extends Array {
     isEmpty() {
     return this.length === 0;
     }
    }
    
    let arr = new PowerArray(1, 2, 5, 10, 50);
    alert(arr.isEmpty()); // false
    
    let filteredArr = arr.filter(item => item >= 10);
    alert(filteredArr); // 10, 50
    alert(filteredArr.isEmpty()); // false

    請注意一件非常有趣的事情。像 filtermap 和其他內置方法 - 返回新的繼承類型的對象。他們依靠 constructor 屬性來做到這一點。

    在上面的例子中,

    arr.constructor === PowerArray

    所以當調用 arr.filter() 時,它自動創建新的結果數組,就像 new PowerArray 一樣,于是我們可以繼續使用 PowerArray 的方法。

    我們甚至可以自定義這種行為。如果存在靜態 getter Symbol.species,返回新建對象使用的 constructor。

    下面的例子中,由于 Symbol.species 的存在,mapfilter等內置方法將返回普通的數組:

    class PowerArray extends Array {
     isEmpty() {
     return this.length === 0;
     }
    
    
     // built-in methods will use this as the constructor
     static get [Symbol.species]() {
     return Array;
     }
    
    }
    
    let arr = new PowerArray(1, 2, 5, 10, 50);
    alert(arr.isEmpty()); // false
    
    // filter creates new array using arr.constructor[Symbol.species] as constructor
    let filteredArr = arr.filter(item => item >= 10);
    
    
    // filteredArr is not PowerArray, but Array
    
    alert(filteredArr.isEmpty()); // Error: filteredArr.isEmpty is not a function

    我們可以在其他 key 使用 Symbol.species,可以用于剝離結果值中的無用方法,或是增加其他方法。

    聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文檔

    ES6Class繼承與super的介紹

    ES6Class繼承與super的介紹:這篇文章主要介紹了關于ES6 Class 繼承與 super的介紹,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下Class 繼承與 superclass 可以 extends 自另一個 class。這是一個不錯的語法,技術上基于原型繼承。要繼承一個對象,需要在 {..}
    推薦度:
    標簽: 繼承 class ES6
    • 熱門焦點

    最新推薦

    猜你喜歡

    熱門推薦

    專題
    Top
    主站蜘蛛池模板: 欧美激情亚洲激情 | 日韩欧美在线综合网高清 | 亚洲综合一区二区精品久久 | 永久毛片 | 黑人粗又大 | 欧美成人高清在线视频大全 | 日韩欧美三区 | 一区二区三区观看 | 九九热免费视频 | 久久精品日日躁夜夜躁欧美 | 国产精品高清视亚洲一区二区 | 在线观看精品国产 | 国产91精品高清一区二区三区 | 国内精品视频在线播放 | 亚洲欧美日韩视频一区 | 久久精品免费一区二区视 | 国产一区二区三区久久精品 | 日韩欧美视频在线播放 | 美日韩一区二区三区 | 成人午夜精品久久久久久久小说 | 欧美另类日韩 | 国产成人无精品久久久 | 欧美日韩国产综合在线 | 欧美精品观看 | 欧美一区二区高清 | 日本国产最新一区二区三区 | 欧美曰韩 | 日韩高清一区二区三区不卡 | 亚洲欧美视频在线观看 | 黄色在线免费观看网址 | 精品久久亚洲一级α | 国产精品久久久久9999 | 日韩高清一区二区 | 91情侣在线偷精品国产 | 欧美激情在线 | 亚洲乱码一二三四区麻豆 | 精品一区二区三区五区六区 | 中文字幕第13亚洲另类 | 国产激情一级毛片久久久 | 欧美日韩综合精品一区二区三区 | 久久久久九九 |