## Class
### 简介
在ES6之前,JavaScript通过构造函数定义并生成新对象:
```js
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` (类)这个概念作为对象的模板,通过 `class` 关键字可以定义类。基本上,它可以看做是一个语法糖。将上面的定义改写:
```js
class Point {
constructor(x, y) {
this.x = x
this.y = y
}
toString() {
return `(${this.x}, ${this.y})`
}
}
```
`constructor` 方法是构造方法,而 `this` 关键字则代表实例对象。
使用的时候也是直接对类使用 `new` 命令,跟构造函数的用法一致。
```js
class Bar {
doStuff() {
console.log('stuff')
}
}
var b = new Bar()
b.doStuff() // 'stuff'
```
构造函数的 `prototpye` 属性在ES6的类上继续存在,类的所有方法都定义在 `prototpye` 属性上。
### 严格模式
类和模块的内部默认使用严格模式,所以不需要使用 `use strict` 指定运行严格模式。
### constructor方法
`constructor` 方法是类的默认方法,通过 `new` 命令生成对象实例时自动调用该方法。一个类必须有 `constructor` 方法,如果没有显式的定义,一个空的 `constructor` 方法会被默认添加。
```js
class Point {}
// 等同于
class Point {
constructor () {}
}
```
`constructor` 方法默认返回实例对象(即`this`).
```js
class Foo {
constructor () {
return Object.create(null)
}
}
new Foo() instanceof Foo
// false
```
### 类的实例对象
生成实例对象的写法跟ES5一致,也是使用 `new` 命令。
### Class表达式
与函数一样,`Class` 也可以使用表达式的形式定义
```js
const MyClass = class Me {
getClassName () {
return Me.name
}
}
```
注意上面的代码,这个类的名字是`MyClass`,而不是`Me`。
### 不存在变量提升
类不存在变量提升。
```js
new Foo() // 报错 ReferenceError
class Foo {}
```
### 私有方法
ES6不提供私有方法,可以通过其他方法来模拟实现
一种方法时通过命名加以区别:
```js
class Widget {
// 公有方法
foo (baz) {
this._bar{baz}
}
// 私有方法
_bar(baz) {
return this.snaf = baz
}
}
```
上面的代码使用一个下划线来标明这个方法是私有方法,但这种命名是不保险的,在类的外部依然可以调用这个方法。
另一种方法时将私有方法移出模块,因为模块内部方法对外都是可见的。
```js
class Widget {
foo (baz) {
bar.call(this, baz)
}
// ...
}
function bar(baz) {
return this.snaf = baz
}
```
还有一种方法,是利用`Symobl`值得唯一性将私有方法的名字命名为一个`Symbol`值。
```js
const bar = Symbol('bar')
const snaf = Symbol('snaf')
export default class myClass {
// 公有方法
foo (baz) {
this[bar] (baz)
}
// 私有方法
[bar](baz) {
return this[snaf] = baz
}
}
```
### 私有属性
ES6也不支持类的私有属性。目前,有一个提案是为`Class`加入私有属性。方法是在属性名之前,使用`#`来表示。
```js
class Point {
#x
constructor (x = 0) {
#x = +x
}
}
```
### this的指向
类的方法内部如果还有`this`,它将默认指向类的实例。
### Class的取值函数(getter)和存值函数(setter)
在类的内部可以使用`get`和`set`关键字对某个属性设置存值函数和取值函数,拦截该属性的存取行为。
```js
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
```
### Class的Generator方法
如果某个方法之前加上星号(*),就表示这个方法时一个`Generator`函数
```js
class Foo {
constructor (...args) {
this.args = args
}
* [Symbol.iterator]() {
for (let arg of this.args) {
yield arg
}
}
}
for (let x of new Foo('hello', 'world')) {
console.log(x)
}
// hello
// wolrd
```
### Class的静态方法
如果在一个方法前加上`static`关键字,就表示该方法不会被实例继承,而是通过类调用,这称为"静态方法"
```js
class Foo {
static classMethod() {
return 'hello'
}
}
Foo.classMethod() // 'hello'
var foo = new Foo()
foo.classMethod() // TypeError
```
父类的静态方法可以被子类继承
```js
class Foo {
static classMethod() {
return 'hello'
}
}
class Bar extends Foo {}
Bar.classMethod() // 'hello'
```
### Class的静态属性和实例属性
静态属性指的是`Class`本身的属性,即`Class.propname`,而不是定义在实例对象(this)上的属性。
```js
class Foo {}
Foo.prop = 1
Foo.prop // 1
```
目前只有像上面的代码那样去给一个类定义静态属性,而不能像定义静态方法那样定义静态属性。
#### Class的实例属性
`Class`的实例属性可以用等式写入类的定义之中。
```js
class MyClass {
myProp = 42
constructor() {
console.log(this.myProp) // 42
}
}
```
#### Class的静态属性
`Class`的静态属性只要在实例属性的定义写法前面加上`static`即可。
```js
class MyClass {
static myStaticProp = 42
constructor() {
console.log(MyClass.myStaticProp) // 42
}
}
```