## 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
}
}
```