js 總結ES6中Class以及繼承

一、Class

1、ES6中的class可以看作是一個語法糖,它的絕大部分功能,ES5都可以做到,新的class寫法只是讓對象原型的寫法更加清晰,更加面向對象編程的語法而已。


class Point { // 定義一個Point類
    constructor(x,y){ // 構造方法
        this.x=x;
        this.y=y;
    }
    // =>類的所有方法都定義在類的prototype屬性上。
    // =>在類的實例上調用方法,其實就是調用原型的方法。
    // =>類的內部所有方法都是不可枚舉的
    toString(){ // 定義toString方法  
        return '('+this.x+','+this.y+')';
    }
}
 // =>Object.assign方法可以方便地一次向類添加多個方法。
 Object.assign(Point.prototype,{
     toValue1(){ return "添加方法1"},
     toValue2(){ return "添加方法2"}
 })

console.log(typeof Point); // =>function =>類的數據類型就是函數
console.log(Point === Point.prototype.constructor) // =>true =>類的本身就指向構造函數

var point1=new Point(); // 使用的時候也是直接對類使用new命令,跟構造函數的用法完全一致。
console.log(point1.toString()); // =>(undefined,undefined)
var point2=new Point(1,1); // 使用的時候也是直接對類使用new命令,跟構造函數的用法完全一致。
console.log(point2.toString()); // =>(1,1)

2、類和模塊的內部默認使用嚴格模式,所以不需要使用 use strict 指定運行模式。只要將代碼寫在類或模塊之中,那麼就只有嚴格模式可用。

3、constructor 方法是類的默認方法,通過 new 命令生成對象實例時自動調用該方法。一個類必須有 constructor 方法,如果沒有顯示定義,一個空的 constructor 方法會被默認添加。

4、類必須使用 new 來調用,否則就會報錯。

5、實例的屬性除非顯示定義在其本身(即 this 對象)上,否則都是定義在原型(即 Class )上。 

6、類的實例共享一個原型對象。

7、Class表達式

// =>Class也可以使用表達式的形式定義。
// =>注意這個類的名字是MyClass而不是Me,Me只在Class的內部代碼可用,指當前類。
const MyClass = class Me{
    getClassName(){
        return Me.name;
    }
}

8、類不存在變量提升

9、ES6不提供私有方法,不支持私有屬性。目前有一個提案爲class加私有屬性。方法是在屬性名前,使用#來表示。

10、類的方法內部如果含有 this,它將默認指向類的實例。

11、name 屬性返回 class 後面的類名。

class Point{

}
console1.log(Point.name); //=>"Point"

12、在類的內部可以使用 get 和 set 關鍵字對某個屬性設置存儲函數和取值函數,攔截該屬性的存取行爲。

 

class MyClass{
    constructor(){
        // ...
    }
    get prop(){
        return 'getter';
    }
    set prop(value){
        console.log('setter'+value);
    }
}
let inst=new MyClass();
inst.prop=123; // =>setter123
console.log(inst.prop); // =>getter

13、存值函數和取值函數是設置在屬性的Descriptor對象上的。

14、如果某個方法之前加上星號(*),就表示該方法是一個Generator函數。

15、類相當於實例中的原型,所有在類中定義的方法都會被實例繼承。如果在一個方法前加上static關鍵字,就表示該方法不會被實例繼承,而是直接通過類調用,稱之爲“靜態方法”。

class Foo{
    static classMethod(){
        return 'hello';
    }
}
console.log(Foo.classMethod()); // =>hello
let foo=new Foo();
foo.classMethod(); // =>Uncaught TypeError: foo.classMethod is not a function

16、父類的靜態方法可以被子類繼承。

 

class Foo{
    static classMethod(){
        return 'hello';
    }
}
class Bar extends Foo{
}

console.log(Bar.classMethod()); // =>hello

17、 靜態方法也可以從super對象上調用。

class Foo{
    static classMethod(){
        return 'hello';
    }
}
class Bar extends Foo{
    static classMethod(){
        return super.classMethod()+',too';
    }
}

console.log(Bar.classMethod()); // =>hello,too

18、靜態屬性指的是Class本身的屬性,即Class.propname,而不是定義在實例對象(this)上的屬性。Class內部只有靜態方法,沒有靜態屬性。正確寫法如下:

class Foo{

}
Foo.prop=1;
console.log(Foo.prop); // =>1

19、Class的實例屬性可以直接用等式寫入類的定義之中。

20、Class的靜態屬性只要在實例屬性寫法前面加上 static 關鍵字就可以了。

21、new.target屬性用於確定構造函數是怎麼調用的。

 

二、ES6中Class的繼承

首先我們要知道一下這些要點:

1、Class通過 extends 關鍵字實現繼承。

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

        ① super 作爲函數調用時表示父類的構造函數,用來新建父類的this對象

        同時需要注意:

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

        b、如果子類沒有定義 constructor 方法,那麼這個方法就會被默認添加。

        c、在子類的構造函數中,只有調用 super 方法之後才能夠使用 this 關鍵字,否則會報錯。

        d、super 雖然代表了父類的構造函數,但是返回的是子類的實例,即 super 內部的 this 指定的是子類。

        e、super () 只能用在子類的構造函數之中,用在其他地方會報錯。

        ② super 作爲對象時在普通方法中指向父類的原型對象;在靜態方法中指向父類

        同時需要注意:

        a、由於 super 指向父類的原型對象,定義在父類實例上的方法或屬性是無法通過 super 調用的。如果定義在父類的原型上,super 就可以取到。

        b、ES6規定,通過 super 調用父類的方法時,super 會綁定子類的 this。

        c、如果通過 super 對某個屬性賦值,這時 super 就是 this,賦值的屬性會變成子類實例的屬性。

        d、如果 super 作爲對象用在靜態方法之中,這時 super 將指向父類,在普通方法之中指向父類的原型對象。

class Point{ // 定義了一個名字爲Point的類
    constructor(x,y){ // constructor是一個構造方法,用來接收參數
        this.x=x;
        this.y=y;
        this.z=1;
        this.a=3;
    }
    toString(){
        return "顏色";
    }
    print(){
        console.log(this.z);
    }
    static myMethod(msg){
        console.log('static',msg);
    }

    myMethod(msg){
        console.log('instance',msg);
    }
}
class ColorPoint extends Point{
    constructor(x,y,color){
        super(x,y); // 調用父類的constructor(x,y)
        this.z=2;
        this.a=4;
        super.a=5;
        this.color=color;
        console.log(super.a);
        console.log(this.a);
    }
    toString(){ // 這是一個類的方法,注意前面沒有function
        return this.color+' '+super.toString(); // 調用父類的toString()
    }
    m(){
        super.print();
    }
    static myMethod(msg){
        super.myMethod(msg);
    }
    myMethod(msg){
        super.myMethod(msg);
    }
}
let color1=new ColorPoint(1,1,"red");
let color2=new ColorPoint(2,2,"blue");
console.log(color1);
console.log(color1.toString());
color1.m();
ColorPoint.myMethod("你好");
color1.myMethod("你好");
console.log("===================");
console.log(color2);
console.log(color2.toString());
color2.m();

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

        ①子類 __proto__ 屬性表示構造函數的繼承,總是指向父類。

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

class A{

}
class B extends A{

}
console.log(B.__proto__ === A); // =>true
console.log(B.prototype.__proto__ === A.prototype); // =>true

        補充:這兩條繼承鏈還可以理解爲:作爲一個對象,子類(B)的原型( __proto__ 屬性)是父類(A);作爲一個構造函數,子類(B)的原型( prototype 屬性)是父類的實例。(感覺很像 組合繼承/僞經典繼承)

4、extends 關鍵字後只要是一個有 prototype 屬性的函數,就能被繼承(除了 Function.prototype 函數)。

下面討論三種特殊情況:

        ①子類繼承Object

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

class B extends Object{

}
console.log(B.__proto__ === Object); // =>true
console.log(B.prototype.__proto__ === Object.prototype); // =>true

        ②不存在任何繼承

        這種情況下,父類作爲一個基類(即不存在任何繼承)就是一個普通函數,所以直接繼承Function.prototype。但是,父類調用後返回的是一個空對象(即 Object 實例),所以 父類.prototype.__proto__ 指向構造函數( Object )的 prototype 屬性。

class B {

}
console.log(B.__proto__ === Function.prototype); // =>true
console.log(B.prototype.__proto__ === Object.prototype); // =>true

        ③子類繼承null

        這種情況和第二種情況很類似。父類作爲一個基類(即不存在任何繼承)就是一個普通函數,所以直接繼承Function.prototype。

class B extends null{

}
console.log(B.__proto__ === Function.prototype); // =>true
console.log(B.prototype.__proto__ === undefined); // =>true

        但是,父類調用後返回的對象不繼承任何方法,所以它的 __proto__ 指向 Function.prototype ,即執行了下面的代碼。

class B extends null{
    constructor(){
        return Object.create(null);
    }
}

5、子類實例的 __proto__ 屬性的 __proto__ 屬性指向父類實例的 __proto__ 屬性。即子類的原型的原型是父類的原型

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章