javascript ES 6 class 詳解

Introduction

上篇文章大體介紹了一些ES6的特性,以及如何在低版本瀏覽器中使用它們。本文是對class的詳解。
譯自Axel Rauschmayer的Classes in ECMAScript 6
另外,若是隻是想測試ES6,能夠到這個網站html

Overview

藉助class 咱們能夠寫出這樣的代碼:es6

class Point {
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }
        toString() {
            return '(' + this.x + ', ' + this.y + ')';
        }
    }
    
    class ColorPoint extends Point {
        constructor(x, y, color) {
            super(x, y);
            this.color = color;
        }
        toString() {
            return super.toString() + ' in ' + this.color;
        }
    }
    
    let cp = new ColorPoint(25, 8, 'green');
    cp.toString(); // '(25, 8) in green'
    
    console.log(cp instanceof ColorPoint); // true
    console.log(cp instanceof Point); // true

Base classes

咱們能夠定義以下的class:瀏覽器

class Point {
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }
        toString() {
            return '(' + this.x + ', ' + this.y + ')';
        }
    }

咱們能夠像使用ES5標準中的constructor同樣實例化classbabel

> var p = new Point(25, 8);
    > p.toString()
    '(25, 8)'

實際上,class仍是用function實現的,並無爲js創造一個全新的class體系。ide

> typeof Point
    'function'

可是,與function相比,它是不能直接調用的,也就是說必須得new出來函數

> Point()
    TypeError: Classes can’t be function-called

另外,它不會像function同樣會被hoisted(緣由是語義階段沒法解析到extends的內容)測試

foo(); // works, because `foo` is hoisted
    function foo() {}
    new Foo(); // ReferenceError
    class Foo {}
function functionThatUsesBar() {
        new Bar();
    }
    
    functionThatUsesBar(); // ReferenceError
    class Bar {}
    functionThatUsesBar(); // OK

與函數同樣,class的定義表達式也有兩種,聲明形式、表達式形式。以前用的都是聲明形式,如下是表達式式的:網站

const MyClass = class Me {
        getClassName() {
            return Me.name;
        }
    };
    let inst = new MyClass();
    console.log(inst.getClassName()); // Me
    console.log(Me.name); // ReferenceError: Me is not defined

Inside the body of a class definition

class定義體是隻能包含方法,不能包含屬性的(標準定義組織認爲原型鏈中不該包含屬性),屬性被寫在constructor中。如下是三種會用到的方法(constructor 、static method、 prototype method):this

class Foo {
        constructor(prop) {
            this.prop = prop;
        }
        static staticMethod() {
            return 'classy';
        }
        prototypeMethod() {
            return 'prototypical';
        }
    }
    let foo = new Foo(123);

以下圖([[Prototype]]表明着繼承關係)當對象被new出來,拿的是Foo.prototype : Object分支,從而能夠調prototype method

constructor,這個方法自己,表明了classprototype

> Foo === Foo.prototype.constructor
    true

constructor有時被稱爲類構造器。相較於ES5,它能夠調用父類的constructor(使用super())。

static methods。它們歸屬於類自己。

> typeof Foo.staticMethod
    'function'
    > Foo.staticMethod()
    'classy'

關於Getters and setters,它們的語法以下:

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

方法名是能夠動態生成的

class Foo() {
        myMethod() {}
    }
    
    class Foo() {
        ['my'+'Method']() {}
    }
    
    const m = 'myMethod';
    class Foo() {
        [m]() {}
    }

增長了迭代器的支持,須要給方法前面加一個*

class IterableArguments {
        constructor(...args) {
            this.args = args;
        }
        * [Symbol.iterator]() {
            for (let arg of this.args) {
                yield arg;
            }
        }
    }
    
    for (let x of new IterableArguments('hello', 'world')) {
        console.log(x);
    }
    
    // Output:
    // hello
    // world

Subclassing

經過extends,咱們能夠繼承其它實現constructor的函數或對象。須要注意一下,constructor與非constructor調用父類方法的途徑是不一樣的。

class Point {
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }
        toString() {
            return '(' + this.x + ', ' + this.y + ')';
        }
    }
    
    class ColorPoint extends Point {
        constructor(x, y, color) {
            super(x, y); // (A)
            this.color = color;
        }
        toString() {
            return super.toString() + ' in ' + this.color; // (B)
        }
    }
    > let cp = new ColorPoint(25, 8, 'green');
    > cp.toString()
    '(25, 8) in green'
    
    > cp instanceof ColorPoint
    true
    > cp instanceof Point
    true

子類的原型就是它的父類

> Object.getPrototypeOf(ColorPoint) === Point
    true

因此,static method也被繼承了

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

static方法也是支持調用父類的。

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

關於子類中使用構造器,須要注意的是,調用this以前,須要調用super()

class Foo {}
    
    class Bar extends Foo {
        constructor(num) {
            let tmp = num * 2; // OK
            this.num = num; // ReferenceError
            super();
            this.num = num; // OK
        }
    }

constructors是能夠被顯示覆蓋(override)的。

class Foo {
        constructor() {
            return Object.create(null);
        }
    }
    console.log(new Foo() instanceof Foo); // false

若是基類中不顯示定義constructor,引擎會生成以下代碼

constructor() {}

對於子類

constructor(...args) {
        super(...args);
    }

The details of classes

  • 類名不能爲eval 或者 arguments,不能有重複的類名,constructor不支持getter,setter。
  • classes不能像函數同樣調用。
  • 原型方法不能用做構造器:
class C {
        m() {}
    }
    new C.prototype.m(); // TypeError

The details of subclassing

ES 6中,子類的使用方法以下:

class Point {
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }
        ···
    }

    class ColorPoint extends Point {
        constructor(x, y, color) {
            super(x, y);
            this.color = color;
        }
        ···
    }

    let cp = new ColorPoint(25, 8, 'green');

原型鏈實現:

> const getProto = Object.getPrototypeOf.bind(Object);

> getProto(Point) === Function.prototype
true
> getProto(function () {}) === Function.prototype
true

> getProto(Point.prototype) === Object.prototype
true
> getProto({}) === Object.prototype
true
相關文章
相關標籤/搜索