ES6 Class 类
ES6中的类实际就是一个函数,且正如函数的定义方式有函数声明和函数表达式两种一样,类的定义也有两种方式.
分别为:
-类声明
-类表达式
类声明
类声明是定义类的一种方式,使用class关键字后跟一个类名,就可以定义一个类.
1 | class Foo { |
不存在变量提升(hoist)
类声明和函数声明不同的一点是,函数声明存在变量提升现象,而类声明不会.即,类必须先声明,然后才可以使用,否则会抛出ReferencError
异常
1 | var foo = new Foo(); // Uncaught ReferenceError: Foo is not defined(...) |
这种规定的原因与类的继承有关,必须保证子类在父类之后定义。
1 | let Foo = class {}; |
上面的代码不会报错,因为class Bar继承Foo时,Foo已经有定义了。(但是,如果存在Class提升,上面代码就会报错,因为Class Bar会被提升到代码头部,而表达式式Foo是不会提升的,所以导致Class Bar继承Foo的时候,Foo还没有定义。)
类表达式
类表达式就定义类的另外一种方式,就像函数表达式一样,在类表达式中,类名是可有可无的。若定义的类名,则该类名只有的类的内部才可以访问到。
1 | // 方式一 |
上面方式二定义类的同时给出了类名,此时,Me类名只可以在Class的内部代码可用,指代当前类。MyClass的name属性值为给出的类名。
1 | let my = new MyClass(); |
采用类表达式,可以写出立即执行的Class。如下:
1 | let person = new class { |
类体和方法定义
类的成员需要定义在一对大括号内{},大括号内的代码的大括号本身组成了类体。类成员包括类构造器和类方法(包括静态方法和实例方法)。
严格模式
类体中的代码都强制在严格模式中执行,即默认”use strict”。考虑到未来所有的代码,其实都是运行在模块之中,所以ES6实际上把整个语言升级到了严格模式。
构造器(constructor方法)
constructor
方法是一个特殊的类方法,它既不是静态方法也不是实例方法,它仅在实例化的时候被调用。一个类只能拥有一个名为constructor
的方法,否则会抛出SyntaxError异常。
如果没有定义constructor
方法,这个方法会被默认添加,即,不管有没有显示定义,任何一个类都有constructor
方法。
子类必须在constructor
方法中调用super
方法,否则新建实例时会报错。因为子类没有自己的this
对象,而是继承父类的this对象,然后对其进行加工,如果不调用super
方法,子类就得不到this对象。
1 | class Point {} |
上面代码中,ColorPoint
继承了父类Point
,但是它的构造函数没有调用super
方法,导致新建实例时报错。
原型方法
定义类的方法时,方法名前面不需要加上function
关键字。另外,方法之间不需要用逗号分隔,加了会报错。
1 | class Bar { |
类的所有方法都是定义在类的`prototype属性上的,上面的写法等同于下面:
1 | Bar.prototype = { |
所以,在类的实例上调用方法,实际上就是调用原型上的方法。
1 | class B {} |
上面代码中,b是B类的实例,它的constructor
方法就是B类原型的constructor
方法。
由于类的方法都是定义在prototype
上面,所以类的新方法可以添加在prototype
对象上面。Object.assign
方法可以很方便地一次向类添加多个方法。
1 | class Point { |
另外,类的内部所有定义的方法,都是不可枚举的(non-enumerable)。
1 | class Point { |
静态方法
static
关键字用来定义类的静态方法。静态方法是指那些不需要对类进行实例化,使用类名就可以直接访问的方法。静态方法经常用来作为工具函数。
1 | class Point { |
静态方法不可以被实例继承,是通过类名直接调用的。但是,父类的静态方法可以被子类继承。
1 | class Foo { |
静态方法也可以用super
关键字调用。
1 | class Foo { |
extends关键字
extends
关键字用于实现类之间的继承。子类继承父类,就继承了父类的所有属性和方法。extends
后面只可以跟一个父类。
super 关键字
super
关键字可以用来调用其父类的构造器或方法。
1 | class Cat { |
类的Getter和Setter方法
与ES5一样,在类内部可以使用get
和set
关键字,对某个属性设置取值和赋值方法。
1 | class Foo { |
上面代码中,prop
属性有对应 的赋值和取值方法,因此赋值和读取行为都被自定义了。
存值和取值方法是设置在属性的descriptor对象上的。
1 | var descriptor = Object.getOwnPropertyDescriptor(Foo.prototype, 'prop'); |
上面代码中,存值和取值方法是定义在prop
属性的描述对象上的,这与ES5一致。
类的Generator方法
如果类的某个方法名前加上星号(*
),就表示这个方法是一个Generator函数。
1 | class Foo { |
上面代码中,Foo类的Symbol.iterator方法前有一个星号,表示该方法是一个Generator函数。Symbol.iterator方法返回一个Foo类的默认遍历器,for...of
循环会自动调用这个遍历器。