es6入門5--class類的基本用法

在ES6以前,準確來講JavaScript語言並沒有類的概念,卻有模擬類的作法。相比在相似java這類傳統面嚮對象語言中經過類來生成實例,js則經過構造函數模擬類來生成實例。java

這是由於在JS設計初期,做者Brendan Eich選擇使用原型來描述對象而非類,但被管理層要求模仿java,所以引入了new this等語言特性,也就是咱們所使用的構造函數作法。es6

那麼自ES6起,JavaScript正式引入了class關鍵字,自此咱們也能夠經過class來定義類了。ecmascript

但須要清楚的是ES6中class只是構造函數的一種語法糖,並不是新鮮玩意,class能實現的,咱們經過ES5構造函數一樣能夠實現。函數

本篇文章只是ES6入門學習筆記,想了解詳細文檔請閱讀阮一峯大神的 ECMAScript 6 入門,那麼本文開始。學習

 1、class寫法與構造函數的部分區別

1.寫法變化this

在ES6以前,咱們模擬一個類的作法是通構造函數spa

let Parent = function (name, age) {
    this.name = name;
    this.age = age;
};
Parent.prototype.sayName = function () {
    console.log(this.name)
};
let child = new Parent('echo', 26);
child.sayName();//echo

ES6 class實現更像類的寫法,咱們改寫上面的方法:prototype

class Parent {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    };
    sayName() {
        console.log(this.name);
    };
};
let child = new Parent('echo', 26);
child.sayName(); //echo

簡單對比下,寫法上主要這幾點變化:設計

1.構造函數名Parent在class寫法時變成了類名,但調用方式不變,依然經過new關鍵字建立實例。code

2.構造函數中this相關操做,在class寫法時概括到了constructor方法中,也就是說ES5的構造函數Parent對應ES6的Parent類中的constructor構造方法。

3.ES5中原型上的方法sayName在ES6 class寫法中直接寫在了內部,同時省略了function關鍵字。

因此對於方法添加,下面這兩種寫法是等效的:

// ES6
class Parent {
    constructor() {};
    sayName() {};
    sayAge() {};
};
// ES5
let Parent = function () {};
Parent.prototype = {
    constructor: function () {},
    toString: function () {},
    sayAge: function () {}
};

能夠看到,ES5寫法同樣能夠一次批量在原型上添加方法,但我發現,class類不能直接這麼作,如下有三種給class類原型添加方法的作法,我分別輸出了它們:

// 寫法一
class Point {
    constructor() {}
    toString() {}
    toValue() {}
};

// 寫法二
class Point {};
Point.prototype = {
    constructor() {},
    toString() {},
    toValue() {},
};

// 寫法三
class Point {};
Point.prototype = {
    constructor: function () {},
    toString: function () {},
    toValue: function () {},
};

經過對比能夠發現,第2、三種寫法先定義class類,再使用prototype在原型上添加方法對於class類無效,方法並無被添加進去。

你確定納悶了,外部添加無效,那爲啥那恰恰有個constructor呢,這是由於constructor方法是class類自帶的

class Parent {

};
//等同於 class Parent { constructor() {} };

也就是說當要給class類原型添加方法時,若是使用ES5的添加作法並不會生效;固然也不是寫在外部就會失效,經過assign方法仍是能夠作到這一點:

class Point {
    constructor() {};
};

Object.assign(Point.prototype, {
    toString() {},
    toValue() {}
});

2.類建立實例必須使用new

經過class類建立實例必須使用new關鍵字,不使用會報錯,這點與構造函數不一樣,在ES5中其實咱們不使用new也能調用構造函數建立實例,雖然這樣作不符合規範。

class Parent {
    constructor() {}
};
let son = Parent();//報錯

let Parent = function (name) {
    this.name = name;
};
let son  = Parent();//不符合規範

3.類的內部方法沒法枚舉

最後一點區別是,class類內部定義的方法沒法枚舉,也就是沒法經過Object.keys方法獲取,但在ES5中keys方法是有效的。

class Point {
    constructor() {};
    toString() {};
    toValue() {};
};
Object.keys(Point);//[]
Object.getOwnPropertyNames(Point.prototype);//['constructor','toString','toValue']

2、constructor方法

前面已經說了,當咱們建立一個類,即使內部沒寫constructor方法,類也會自帶。

因爲類的方法都在原型上,因此當咱們調用實例上的方法等同於調用類原型上的方法:

class Parent {
    constructor() {}
};
let son = new Parent();
console.log(son.sayName === Parent.prototype.sayName);//true

類實例的constructor指向類,這與構造函數實例的constructor指向構造函數自己保持一致:

//類的實例與構造函數實例的constructor屬性都指向建立本身的類或構造函數
class Parent {
    constructor() {}
};
let son = new Parent();
console.log(son.constructor);//class Parent

let Parent = function (name) {
    this.name = name;
};
let son  = new Parent();
console.log(son.constructor);//Parent

類與構造函數prototype對象的constructor屬性都會指向本身,這點也保持了一致。

//類與構造函數prototype的constructor屬性都指向本身
class Parent {
    constructor() {}
};
console.log(Parent.prototype.constructor === Parent);//true

let Parent = function (name) {
    this.name = name;
};
console.log(Parent.prototype.constructor === Parent);//true

其實到這裏,class類基本用法算說完了,下面主要是對於class概念其它補充。

3、class類的其它補充

1.class類中的存值取值函數

在類的內部也可使用get與set方法對某個屬性的取值存值操做作攔截處理。

class Parent {
    get name() {
        return 'echo'
    };
    set name(val) {
        console.log(val);
    };
};
let son = new Parent();
son.name = '時間跳躍'; //時間跳躍
son.name; //echo

當咱們存值和取值時,其實是調用了class內部的get與set對應方法,這點與ES5保持一致。

2.class內中的屬性名可使用變量

let name = 'sayName';
class Parent {
    [name]() {
        console.log('echo');
    }
};
let son = new Parent();
son[name]();//echo

3.class表達式寫法

let Parent = class {
    sayName() {
        console.log('echo');
    }
};
let son = new Parent();
son.sayName(); //echo

與函數表達式相同,class類在表達式寫法下也能添加一個class名:

let Parent = class Me{
    sayName() {
        console.log('echo');
    }
};
let son = new Parent();
son.sayName(); //echo
console.log(Parent.name);//Me

那麼此時,Parent類的name屬性爲Me,但建立實例你得new Parent,而非new Me();

 4.類中this指向

類方法中自帶嚴格模式,且類方法中的this默認指向類的實例

class Parent {
    sayName() {
        console.dir(this); //Patent {}
        this.sayAge(26);
    };
    sayAge(age) {
        console.log(age, this);
    }
};
let son = new Parent();
son.sayName(); //26 Patent {}
console.log(Parent); //class Parent

但若是你在內部方法單獨抽出來在外部調用,this此時會指向undefined(嚴格模式);

class Parent {
    sayName() {
        console.log(this);//undefined
        this.sayAge(26);
    };
    sayAge(age) {
        console.log(age);
    }
};
let son = new Parent();
let {sayName} = son;
sayName();//報錯

解決方式是,能夠經過在class類中的constructor構造方法中直接爲你須要外部調用的方法綁定this。

class Parent {
    constructor(){
        this.sayName = this.sayName.bind(this);
    }
    sayName() {
        this.sayAge(26)
    };
    sayAge(age) {
        console.log(age);
    }
};
let son = new Parent();
let {sayName} = son;
sayName();//26

或者在構造方法中利用箭頭函數,由於箭頭函數中的this指向自身定義時所在的對象,此時箭頭函數this指向實例。

class Parent {
    constructor() {
        this.sayName = () => {
            this.sayAge(26);
        }
    }
    sayAge(age) {
        console.log(age);
    }
};
let son = new Parent();
let {sayName} = son;
sayName(); //26

5.類的靜態方法

我在JavaScript模式一書的讀書筆記中也有提到ES5中的構造函數靜態方法;若是某個方法只有類自身能夠調用,實例並不會繼承,那麼咱們通常稱此方法爲靜態方法。

//ES5
function Parent (){};
Parent.sayAge = function (){
    console.log(26);
};
Parent.sayAge()//26
let son = new Parent();
son.sayAge()//報錯,找不到這個方法

//ES6
class Parent {
    static sayAge() {
        console.log(26);
    }
};
Parent.sayAge()//26
let son = new Parent();
son.sayAge()//報錯,找不到這個方法

在上述代碼中,我分別用ES5與ES6兩種寫法分別爲類(構造函數)添加了靜態方法sayAge。

很明顯這個方法只能被類自身調用,實例沒法繼承,只是相比ES5直接添加在構造函數上,類使用了static字段,也就是說,若是你想讓某個方法做爲靜態方法,請在前面添加static。

另外有一點,若是靜態方法中使用了this,此時this指向了類,而不是實例,請注意。

//ES5
function Parent (){};
Parent.sayAge = function (){
    console.log(this);
};
Parent.sayAge()//構造函數自身

//ES6
class Parent {
    static sayAge() {
        console.log(this);
    }
};
Parent.sayAge()//class Patent 類自身

6.類的靜態屬性

同理,在ES5中,我在構造函數內部某個屬性只想給構造函數自身使用,實例沒法使用,此時就得使用靜態屬性

//ES5
function Patent(){};
Patent.age = 26;
Patent.sayAge = function (){
    console.log(this.age);
}
Patent.sayAge();//26

//ES6
class Parent {
    static sayAge() {
        console.log(this.age);
    };
};
Parent.age = 26;
Parent.sayAge();//26

上述代碼中分別用ES5,ES6爲類添加了靜態屬性,其實都是直接加在類上,作法相同。

固然在後面的提案中,也推薦使用static來建立類的靜態屬性,作法與靜態方法相同,也就是說有兩種方法能夠作到這點。

class Parent {
    static age = 26;
    static sayAge() {
        console.log(this.age);
    };
};
Parent.sayAge();//26

7.實例屬性的簡寫方法

我在前面說,當聲明一個類,即便不寫constructor方法,類也會自帶,那我想省略掉constructor方法,同時還想給實例添加屬性呢怎麼辦,其實也能夠簡寫。

class Parent {
    constructor() {
        this.name = 'echo';
        this.age = 26;
    };
    sayName() {};
};
let son = new Parent('echo', 26);
son.name //echo
son.age //26

class Parent {
    name = 'echo';
    age = 'age';
    sayName() {};
};
let son = new Parent();
son.name //echo
son.age //26

以上兩種寫法等效,第二種實例賦值時直接寫在了類的頂部,同時去掉了this,固然若是實例賦值帶參數,那就無法簡寫了。

最後有一個類的私有屬性和私有方法沒說,由於ES6沒提供,只能模擬,有興趣能夠自行閱讀。

那麼ES6中class類基本用法就說到這裏了。

相關文章
相關標籤/搜索