【讀書筆記】ES6 Class 繼承

来源:https://www.cnblogs.com/linxian95/archive/2018/12/04/10056291.html
-Advertisement-
Play Games

繼承 1.Class可以通過extends關鍵字實現繼承。 繼承類的所有屬性和方法。 2.constructor方法和toString方法之中,都出現了super關鍵字,它在這裡表示父類的構造函數,用來新建父類的this對象。 3.子類必須在constructor方法中調用super方法,否則新建實 ...


繼承

 

class Point {
}
class ColorPoint extends Point {
}

1.Class可以通過extends關鍵字實現繼承

繼承類的所有屬性和方法

 

class ColorPoint extends Point {
    constructor(x, y, color) {
        super(x, y); // 調用父類的constructor(x, y)
        this.color = color;
    }
    toString() {
        return this.color + ' ' + super.toString(); // 調用父類的toString()
    }
}

2.constructor方法和toString方法之中,都出現了super關鍵字,它在這裡表示父類的構造函數,用來新建父類的this對象。

 

class Point { ... }
class ColorPoint extends Point {
    constructor() {}
}
let cp = new ColorPoint(); // ReferenceError

3.子類必須在constructor方法中調用super方法,否則新建實例時會報錯。

這是因為子類自己的this對象,必須先通過父類的構造函數完成塑造,得到與父類同樣的實例屬性和方法,然後再對其進行加工,加上子類自己的實例屬性和方法。

如果不調用super方法,子類就得不到this對象。

ES5的繼承,實質是先創造子類的實例對象this,然後再講父類的方法添加到this上面。

ES6實質是先將父類實例對象的屬性和方法,加到this上面,然後再用子類的構造函數修改this。

 

class ColorPoint extends Point {
}
// 等同於
class ColorPoint extends Point {
    constructor(...args) {
        super(...args);
    }
}

4.如果子類沒有定義constructor方法,這個方法會被預設添加

不管有沒有顯式定義,任何一個子類都有constructor方法。

 

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
}
class ColorPoint extends Point {
    constructor(x, y, color) {
        this.color = color; // ReferenceError
        super(x, y);
        this.color = color; // 正確
    }
}

5.在子類的構造函數中,只有調用super之後,才可以使用this關鍵字

這是因為子類實例的創建,基於父類實例,只有super方法才能調用父類實例。

 

let cp = new ColorPoint(25, 8, 'green');
cp instanceof ColorPoint // true
cp instanceof Point // true

6.實例對象同時是兩個類的實例。

 

class A {
    static hello() {
        console.log('hello world');
    }
}
class B extends A {
}
B.hello(); // hello world

7.父類的靜態方法會被子類繼承

 


 

Object.getPrototypeOf()

 

Object.getPrototypeOf(ColorPoint) === Point

1.Object.getPrototypeOf方法可以用來從子類上獲取父類

因此可以使用這個方法判斷一個類是否繼承了另一個類。

 


 

super關鍵字

 

1.super關鍵字既可以當作函數使用,也可以當作對象使用。

 

class A {}
class B extends A {
    constructor() {
        super();
    }
}

2.super作為函數調用時,代表父類的構造函數

ES6要求子類的構造函數必須執行一個super函數。

 

class A {
    constructor() {
        console.log(new.target.name);
    }
}
class B extends A {
    constructor() {
        super();
    }
}
new A() // A
new B() // B

3.super雖然代表了父類A的構造函數,但是返回的是子類B的實例,即super內部的this指的是B,因此super()在這裡相當於A.prototype.constructor.call(this);

 

class A {}
class B extends A {
    m() {
        super(); //報錯
    }
}

4.作為函數時,super()只能用在子類的構造函數之中,用在其他地方就會報錯。

 

class A {
    P() {
        return 2;
    }
}
clas Bextends A {
    constructor() {
        super();jin
        console.log(super.p()); // 2
    }
}
let b = new B();

5.super作為對象時,在普通方法中,指向父類的原型對象在靜態方法中,指向父類

 

class A {
    constructor() {
        this.p = 2;
    }
}
class B extends A {
    get m() {
        return super.p;
    }
}
let b = new B();
b.m // undefined

6.由於super指向父類的原型對象,所以定義在父類實例上的方法或屬性,是無法通過super調用的。

 

class A {]
A.prototype.x = 2;
class B extends A {
    constructor() {
        super();
        console.log(super.x); // 2
    }
}
let b = new B();

7.如果屬性定義在父類的原型對象上,super就可以取到。

 

class A {
    constructor() {
        this.x = 1;
    }
    print() {
        console.log(this.x);
    }
}
class B extends A {
    constructor() {
        super();
        this.x = 2;
    }
    m() {
        super.print();
    }
}
let b = new B();
b.m() // 2

8.ES6規定,在子類普通方法中通過super調用父類的方法時,方法內部的this指向當前的子類實例。

也就是說,實際上執行的是super.print.call(this);

 

class A {
    constructor() {
        this.x = 1;
    }
}
class B extends A {
    constructor() {
        super();
        this.x = 2;
        super.x = 3;
        console.log(super.x); // undefined
        console.log(this.x); // 3
    }
}
let b = new B();

9.由於this指向子類實例,所以如果通過super對某個屬性賦值,這時super就是this,賦值的屬性會變成子類實例的屬性。

而當讀取super.x的時候,讀的是A.prototype.x,所以返回undefined。

 

class Parent {
    static myMethod(msg) {
        console.log('static', mag);
    }
    myMethod(mag) {
        console.log('instance', msg);
    }
}
class Child extends Parent {
    static myMethod(msg) {
        super.myMethod(msg);
    }
    myMethod(mag) {
        super.myMethod(msg);
    }  
}
Child.myMethod(1); // static 1
var child  = new Child();
child.myMethod(2); // instance 2

10.如果super作為對象,用在靜態方法之中,這時super將指向父類,而不是父類的原型對象。

 

class A {
    constructor() {
        this.x = 1;
    }
    static print() {
        console.log(this.x);
    }
}
class B extends A {
    constructor() {
        super();
        this.x = 2;
    }
    static m() {
        super.print();
    }
}
B.x = 3;
B.m() // 3

11.另外,在子類的靜態方法中通過super調用父類的方法時,方法內部的this指向當前的子類,而不是子類的實例。

 

class A {}
class B extends A {
    constructor() {
        super();
        console.log(super); // 報錯
    }
}

12.註意,使用super的時候,必須顯式指定是作為函數、還是作為對象使用,否則會報錯。

 

var obj = {
    toString() {
        return "MyObject: " + super.toString();
    }
};
obj.toString(); // MyObject: [object Object]

13.由於對象總是繼承其他對象的,所以可以在任意一個對象中,使用super關鍵字。

 


 

類的prototype屬性和__proto__屬性

 

1.Class作為構造函數的語法糖,同時有prototype屬性和__proto__屬性,因此同時存在兩條繼承鏈。

大多數瀏覽器的ES5實現之中,每一個對象都有__proto__屬性,指向對應的構造函數的prototype屬性

 

class A {
}
class B extends A {
}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true

2.子類的__proto__屬性,指向父類,表示構造函數的繼承。

子類的prototype屬性的__proto__屬性,指向父類的prototype屬性,表示方法的繼承。

 

class A {
}
class B {
}
Object.setPrototypeOf(B.prototype, A.prototype); // B的實例2繼承A的實例
Object.setPrototypeOf(B, A); // B繼承A的靜態屬性
const b = new B();

3.類的繼承模式。

 

Object.setPrototypeOf = function(obj, proto) {
    obj.__proto__ = proto;
    return obj;
};
Object.setPrototypeOf(B.prototype, A.prototype);
// 等同於
B.prototype.__proto__ = A.prototype;

Object.setPrototypeOf(B, A);
// 等同於
B.__proto__ = A;

4.Object.setPrototypeOf的實現方法。

 

Object.create(A.prototype);
// 等同於
B.prototype.__proto__ = A.prototype;

5.作為一個對象,子類的原型(__proto__)是父類;作為一個構造函數,子類的原型對象(prototype屬性)是父類原型對象的實例。

 

class B extends A {
}

6.extends關鍵字後面可以跟多種類型的值。

上面代碼的A,只要是一個有prototype屬性的函數,就能被B繼承。

由於函數都有prototype屬性,因此A可以是任意函數。

 

class A extends Object {
}
A.__proto__ === Object // true
A.prototype.__proto__ === Object.prototype // true

7.第一種情況,子類繼承Object類。

這種情況下,A其實就是構造函數Object的複製,A的實例就是Object的實例。

 

class A {
}
A.__proto__ === Function.prototype // true
A.prototype.__proto__ === Object.prototype // true

8.第二種情況,不存在任何繼承。

A作為一個基類,就是一個普通函數,所以直接繼承Function.prototype。

但是,A調用後返回一個空對象,所以A.prototype.__proto__指向構造函數的prototype屬性。

 


 

實例的__proto__屬性

 

var p1 = new Point(2, 3);
var p2 = new ColorPoint(2, 3, 'red');
p2.__proto__ === p1.__proto // false
p2.__proto__.__proto__ === p1.__proto__ // true

1.子類實例的__proto__屬性的__proto__屬性,指向父類實例的__proto__屬性。

也就是說,子類的原型的原型,是父類的原型。

 

p2.__proto__.__proto__.printName = function() {
    console.log('Ha');
};
p1.printName() // "Ha"

2.通過子類實例的__proto__.__proto__屬性,可以修改父類實例的行為。

 


 

原生構造函數的繼承

 

原生構造函數是指語言內置的構造函數,通常用來生成數據結構。

ES的原生構造函數大致有

  • Boolean()
  • Number()
  • String()
  • Array()
  • Date()
  • Function()
  • RegExp()
  • Error()
  • Object()

 

class MyArray extends Array {
    constructor(...args) {
        super(...args);
    }
}
var arr = new MyArray();
arr[0] = 12;
arr.length // 1
arr.length = 0;
arr[0] // undefined

1.ES6允許繼承原生構造函數定義子類,ES6是先新建父類的實例對象this,然後再用子類的構造函數修飾this,使得父類的所有行為都可以繼承。

這意味著,ES6可以自定義原生數據結構的子類,這是ES5無法做到的。

 

class VersionedArray extends Array {
    constructor() {
        super();
        this.history = [[]];
    }
    commit() {
        this.history.push(this.slice());
    }
    revert() {
        this.splice(0, this.length, ...this.history[this.history.length - 1]);
    }
}
var x = new VersionedArray();
x.push(1);
x.push(2);
x // [1, 2]
x.history // [[]]
x.commit();
x.history // [[], [1, 2]]
x.push(3);
x // [1, 2, 3]
x.history // [[], [1, 2]]
x.revert();
x // [1, 2]

2.extends關鍵字不僅可以用來繼承類,還可以用來繼承原生的構造函數。

因此可以在原生數據結構的基礎上,定義自己的數據結構。

 

class ExtendableError extends Error {
    constructor(message) {
        super();
        this.message = message;
        this.stack = (new Error()).stack;
        this.name = this.constructor.name;
    }
}
class MyError extends ExtendableError {
    consturctor(m) {
        super(m);
    }
}
var myerror = new MyError('11');
myerror.message // "11"
myerror instanceof Error // true
myerror.name // "MyError"
myerror.stack // Error at myError.enxtendableError

3.自定義Error子類的例子,可以用來定製報錯時的行為。

 

class NewObj extends Object {
    constructor() {
        super(...arguments);
    }
}
var o = new NewObj({attr: true});
o.attr === true // false

4.繼承Object的子類,有一個行為差異。

NewObj繼承了Object,但是無法通過super方法向父類Object傳參。

這是因為ES6改變了Object構造函數的行為,一旦發現Object方法不是通過new Object()這種形式調用,ES6規定Object構造函數會忽略參數。

 


 

Mixin模式的實現

 

const a = {
    a: 'a'
};
const b = {
    b: 'b'
};
const c = {...a, ...b}; // {a: 'a', b: 'b'}

1.Mixin指的是多個對象合成一個新的對象,新對象具有各個組成成員的介面。

 

function mix(...mixins) {
    class Mix {}
    for (let mixin of mixins) {
        copyProperties(Mix.prototype, mixin); // 拷貝實例屬性
        copyProperties();
    }
    return Mix;
}
function copyProperties(target, source) {
    for (let key of Reflect.ownKeys(source)) {
        if (key !== "constructor"
        && key !== "prototype"
        && key !== "name") {
            let desc = Object.getOwnPropertyDescriptor(source, key);
            Object.definedProperty(target, key, desc);
        }
    }
}
class DistributedEdit extends mix(Loggable, Serializable) {
    ...
}

2.將多個類的介面“混入”另一個類。

 

總結1

繼承
1.通過extends
繼承所有屬性和方法
2.super()表示父類的constructor()
super表示父類
3.子類必須在constructor()中調用super()
4.子類預設添加constructor()
5.子類 constructor() super() this
6.實例是兩個類的實例
7.父類的靜態方法會被子類繼承

Object.getPrototypeOf()
1.獲取子類的父類

super
1.super: super() / super
2.super()→父類的constructor() ×
3.super()→父類的constructor()→返回子類的實例
4.super()只能用在子類的constructor()
5.super 靜態方法→父類
super 普通方法→父類的prototype
6.父類實例的方法或屬性無法通過super調用
7.父類prototype上的方法或屬性可以通過super調用
8.子類 普通方法 super調用父類方法 this指向子類實例
9.子類 普通方法 通過super對屬性賦值 = 對子類實例屬性賦值
10.子類 靜態方法 super→父類 ×
11.子類 靜態方法 super調用父類方法 this指向子類
12.使用super必須顯式的指定作為函數還是作為對象
13.可以在任意對象中使用super關鍵字

 

總結2

子類必須在constructor()中調用super()
子類必須在constructor()中調用super()後才能使用this
super()表示父類的constructor()
super()表示父類的constructor()但返回的是子類的實例
super()只能用在子類的constructor()
super在普通方法中指向父類的prototype
super在普通方法中調用父類的方法,this指向子類實例
super在普通方法中對屬性賦值等於對子類的實例賦值
super在靜態方法中指向父類
super在靜態方法中調用父類的方法,this指向子類

如何調用父類的方法或屬性?
父類prototype上的方法或屬性可以通過super調用。
如何調用父類實例的方法或屬性?
父類實例的方法或屬性無法通過super調用。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 類數組對象:arguments總所周知,js是一門相當靈活的語言。當我們在js中在調用一個函數的時候,我們經常會給這個函數傳遞一些參數,js把傳入到這個函數的全部參數存儲在一個叫做arguments的東西裡面,那麼這到底是什麼東西?在js中萬物皆對象,甚至數組字元串函數都是對象。所以這個叫做argu ...
  • js代碼: ...
  • /* * 中間就可以進行封裝操作 * mui就代表mui,owner就代表window的app屬性,就是一個傳值 */ (function(mui,owner) { /** * 獲取當前狀態 **/ owner.getState = function() { var stateText = plus ...
  • 本文由QQ音樂前端團隊發表 前段時間做了一個非常有意思的模擬終端的展示頁:http://ursb.me/terminal/(沒有做移動端適配,請在PC端訪問),這個頁面非常有意思,它可以作為個人博客系統或者給 Linux 初學者學習終端命令,現分享給大家~ 開源地址:airingursb/termi ...
  • 一直以來,我都以為我已經懂了JavaScript中 閉包 的概念,直到有一次小伙伴突然問我這個概念的時候,我才發現我根本不知道該怎來麽跟他來講述這個概念。 那時候我就知道我是自我欺騙,打腫臉充胖子了。 所以,花了點時間去專門瞭解了一下,今天專門記錄一下自己所理解的閉包。 一. 概念 閉包,簡單來講, ...
  • 編碼 首先練習數字相關的一些操作: 基於如上HTML,實現需求 按照HTML中按鈕的描述以此實現功能 計算結果顯示在 id 為 result 的 P 標簽中 除了第一個按鈕,其它按鈕操作時,都需要判斷輸入是否為數字,否則在 console 中輸出錯誤信息 註意點:Math.round()判斷有小數點 ...
  • input 輸入框組件 源碼: html <! 當type的值不等於textarea時 <! 前置元素 <! 核心部分:輸入框 <! input框內的頭部的內容 <! prefixIcon頭部圖標存在時,顯示i標簽 <! input框內的尾部的內容 <! showClear為false時,顯示尾部圖 ...
  • 聲明 本系列文章內容全部梳理自以下幾個來源: 《JavaScript權威指南》 "MDN web docs" "Github:smyhvae/web" "Github:goddyZhao/Translation/JavaScript" 作為一個前端小白,入門跟著這幾個來源學習,感謝作者的分享,在其基 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...