javascript学习指南|javascript ES6中class 使用详解【翻译】

更新时间:2020-01-26    来源:php常用代码    手机版     字体:

【www.bbyears.com--php常用代码】

Overview

借助class 我们可以写出这样的代码:

  

 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一样实例化class

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

实际上,class还是用function实现的,并没有为js创造一个全新的class体系。

    > 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):

    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,这个方法本身,代表了class

    > 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



[译]JavaScript ES6 class指南
 
 EcmaScript 2015 (又称ES6)通过一些新的关键字,使类成为了JS中一个新的一等公民。但是目前为止,这些关于类的新关键字仅仅是建立在旧的原型系统上的语法糖,所以它们并没有带来任何的新特性。不过,它使代码的可读性变得更高,并且为今后版本里更多面向对象的新特性打下了基础。

这样做的原因是为了保证向后兼容性。也就是,旧代码可以在不做任何hack的情况下,与新代码同时运行。


定义类

让我们回想一下在ES5中定义一个类的方式。通过不是很常用的 Object.defineProperty 方法,我可以定义一些只读的属性。


function Vehicle(make, year) { Object.defineProperty(this, 'make', { get: function() { return make; } }); Object.defineProperty(this, 'year', { get: function() { return year; } }); } Vehicle.prototype.toString = function() { return this.make + ' ' + this.year; } var vehicle = new Vehicle('Toyota Corolla', 2009); console.log(vehicle.make); // Toyota Corolla vehicle.make = 'Ford Mustang'; console.log(vehicle.toString()) // Toyota Corolla 2009

很简单,我们定义了一个有两个只读属性和一个自定义 toString 方法的 Vehicle 类。让我们在ES6中来做一样的事情:


class Vehicle { constructor(make, year) { this._make = make; this._year = year; } get make() { return this._make; } get year() { return this._year; } toString() { return `${this.make} ${this.year}`; } } var vehicle = new Vehicle('Toyota Corolla', 2009); console.log(vehicle.make); // Toyota Corolla vehicle.make = 'Ford Mustang'; console.log(vehicle.toString()) // Toyota Corolla 2009

上面两个例子中定义的类有一个不同的地方。我们为了享受新的 get 语法带来的好处,所以只是将 make 和 year 定义成了普通的属性。这使它们可以被外部所改变。如果你确实需要一个严格的私有属性,还是请继续使用 defineProperty 。


类声明

在ES6中,有两个声明类的方式。第一种方法叫作 类声明,这也是我们在上述例子中使用的方式。


class Vehicle() { }

有一个需要注意的地方是,类声明与函数声明不同,它不会被提升(hoisted)。例如,以下的代码工作正常:
console.log(helloWorld()); function helloWorld() { return "Hello World"; }

但是,以下代码会抛出一个异常:
var vehicle = new Vehicle(); class Vehicle() { } 类表达式

另一个定义类的方式叫做 类表达式。它与函数表达式的运行方式完全一样。一个类表达式可以是具名的也可以是匿名的。


var Vehicle = class { } var Vehicle = class VehicleClass { constructor() { // VehicleClass is only available inside the class itself } } console.log(VehicleClass); // throws an exception 静态方法

static 关键字是ES6的另一个语法糖,它使静态方法声明也成为了一个一等公民。在ES5中,静态方法就像是构造函数的一个属性。


function Vehicle() { // ... } Vehicle.compare = function(a, b) { // ... }

在使用了新的 static 关键字后:


class Vehicle { static compare(a, b) { // ... } }

在底层, javascript 所做的,也只是将这个方法添加为 Vehicle 构造函数的一个属性。值得注意的是,你也可以用同样的语法为类添加静态属性。


类继承

旧的原型继承有时看起来让人非常头疼。ES6中新的 extends 关键字解决了这个问题。在ES5,我们是这么做的:


function Motorcycle(make, year) { Vehicle.apply(this, [make, year]); } Motorcycle.prototype = Object.create(Vehicle.prototype, { toString: function() { return 'Motorcycle ' + this.make + ' ' + this.year; } }); Motorcycle.prototype.constructor = Motorcycle;

使用的新的 extends 关键字,看上去就清晰多了:


class Motorcycle extends Vehicle { constructor(make, year) { super(make, year); } toString() { return `Motorcycle ${this.make} ${this.year}`; } }

super 关键字也可以用于静态方法:


class Vehicle { static compare(a, b) { // ... } } class Motorcycle { static compare(a, b) { if (super.compare(a, b)) { // ... } } } super关键字

上一个例子也展示了新的 super 关键字的用法。当你想要调用父类的函数时,这个关键字就显得十分好用。

在想要调用父类的构造函数时,你可以简单地将 super 关键字视作一个函数使用,如 super(make, year) 。对于父类的其他函数,你可以将 super 视作一个对象,如 super.toString() 。例子:


class Motorcycle extends Vehicle { toString() { return 'Motorcycle ' + super.toString(); } } 可被计算的方法名

当在 class 中声明属性时,定义属性名时,你可以使用表达式。这个语法特性在一些 ORM 类库中将会非常流行。例子:


function createInterface(name) { return class { ['findBy' + name]() { return 'Found by ' + name; } } } const Interface = createInterface('Email'); const instance = new Interface(); console.log(instance.findByEmail());


最后

在当前,使用 class 关键字来声明类,而不使用原型,获得的仅仅是语法上的优势。但是,这个是一个适应新语法和新实践的好开始。 JavaScript 每天都在变得更好,并且通过 class 关键字,可以使各种工具更好得帮助你。

本文来源:http://www.bbyears.com/jiaocheng/84898.html

热门标签

更多>>

本类排行