ES6 Class
ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
简介
ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
简单的对比
// ES5
function Point(x, y) {
    this.x = x;
    this.y = y;
}
Point.prototype.toString = function () {
    return '(' + this.x + ', ' + this.y + ')';
}
var p = new Point(1, 2);
// ES6
class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    toString() {
        return '(' + this.x + ', ' + this.y + ')';
    }
}
let p = new Point(1, 2)
上述的代码分别定义了在 ES5 和 ES6 中类声明的方式,可以看出有几点区别:
ES5用function声明类,ES6使用class声明。ES5中函数中的内容移到了ES6/constructor中。ES5中在prototype上配置原型链上的方法,在ES6中直接定义。
由上可以看出,ES6 中的 class 一定程度上可以认为是 ES5 类写法的语法糖。
接着思考下 ES5 中类是如何实例化的:
- 直接调用类的函数,然后把 
this对象给返回出来。 - 把 
prototype上的内容定义在实例的原型链上。 
首先我们来看第一点,在 ES5 的类编写过程中,总会有这么一句
Point.prototype.constructor === Point // true
这说明类 prototype 上的 constructor 属性是自己。
那现在在来看实例化过程中第一步,可以认为调用类下 prototype 上的 constructor 方法。
那么是否可以这么认为:一个类的实例化过程其实和类本身没多大关系了,仅仅和 prototype 有关。
接着在来看 ES6 中的构造方式,我们定义了一个名字和一系列的方法,这其实就是在定义一个类的 prototype 。
但有一点需要尤其注意:
- 在 
ES5中在prototype上定义的方法是可枚举的,ES6中定义的所有方法都是不可枚举的。 
类的属性名,可以用 [] 表示:
let methodName = 'getArea';
class Square {
    constructor(length) {
        // ...
    }
    [methodName]() {
        // ...
    }
}
constructor 方法
constructor 方法是类的默认方法,通过 new 命令生成对象实例时,会调用该方法。一个类必须有 constructor 方法,如果没有显式定义,一个空的 constructor 方法会被默认添加。
class Point {
}
// 等同于
class Point {
    constructor() {}
}
constructor 方法默认返回实例对象(this),当然也可以指定对象返回,但是这就会照成实例对象并不是类的实例。
class Foo {
    constructor() {
        return Object.create(null)
    }
}
new Foo() instanceof Foo
// false
Class 表达式
Class 表达式的效果与函数一致
const MyClass = class Me {
    getClassName() {
        return Me.name
    }
};
let inst = new MyClass();
inst.getClassName();                // Me
Me.name;                            // ReferenceError: Me is not defined
如果一个类被定义成表达式的形式,那么跟在 class 关键字后的类名在类外部不可见,仅仅只能在类内部使用,如果内部不使用的话,可以去掉。
采用 Class 表达式,可以写出立即执行的 Class。
let person = new class {
    constructor(name) {
        this.name = name
    }
    
    sayName() {
        console.log(this.name)
    }
}('张三')
person.sayName();               // "张三"
但感觉这很没必要,这还不如直接写一个对象。
不存在变量提升
类在 ES6 中的定义方式与 let 变量一致,不存在变量提升。
new Foo();                      // ReferenceError
class Foo {};
this 指向
在 ES6 实例对象下的方法表现与 ES5 一致,在方法中使用 this 对象需要小心,this 代指拥有这个方法的对象。
class Logger {
    printName(name = 'there') {
        this.print(`Hello ${name}`);
    }
    
    print(text) {
        console.log(text);
    }
}
const logger = new Logger();
const { printName } = logger;
printName();
// TypeError: Cannot read property 'print' of undefined
上述写法会报错,原因在于声明的 printName 变量的拥有者是 window 。这与函数下的 this 对象的绑定一致,并不存在说 ES6 下有特殊情况,解决方式和函数确定 this 指向方式一致。
- 使用 
bind。 - 使用箭头函数。
 
不过使用这两种方法,都需要在 this 对象上绑定同名的原型链上的方法:
class Logger {
    constructor() {
        this.printName = this.printName.bind(this);
    }
    // ...
}
class Logger {
    constructor() {
        this.printName = (name = 'there') => {
            this.print(`Hello ${name}`);
        }
    }
    // ...
}
Class get/set
与 ES5 一样,在 类 的内部可以使用 get 和 set 关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。
class MyClass {
    constructor() {
        // ...
    }
    get prop() {
        return 'getter';
    }
    set prop(value) {
        console.log('setter: ' + value);
    }
}
let inst = new MyClass();
inst.prop = 123;                    // setter: 123
inst.prop;                          // 'getter'
这其实也是在语法糖内的简单实现,ES5 中通过 defineProperty 定义 prototype 可以实现一样的效果。
Class 的静态方法
类相当于实例的原型,所有在类中定义的方法,都会被实例实现。如果在一个方法前,加上 static 关键字,就表示该方法不会被实例实现,而是直接通过类来调用,这就称为 静态方法。
class Foo {
    static classMethod() {
        return 'hello';
    }
}
Foo.classMethod();                  // 'hello'
var foo = new Foo();
foo.classMethod();
// TypeError: foo.classMethod is not a function
当然如果在静态方法包含 this 关键字,这个 this 指的是类,而不是实例,这其实也是遵循函数执行时的 this 指向问题,总是执行调用这个函数的对象,而类仅仅是一个特殊的对象。
父类的静态方法,可以被子类继承。
class Foo {
    static classMethod() {
        return 'hello';
    }
}
class Bar extends Foo {
}
Bar.classMethod();                  // 'hello'
但目前 ES6 规定,类下仅仅只能有静态方法,不能有静态属性。