Class
coderljw 2024-10-13 大约 3 分钟
# 1. class
ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
constructor 必须有,未显式定义时 JavaScript 引擎会默认添加,默认返回实例对象(this)。
class 声明的方法默认定义在原型链 prototype 上,不可枚举。
class XueDao {
wife = '南宫仆射'
constructor(name, age) {
this.name = name
this.age = age
}
rank(ranking) {
return `天下第${ranking}${this.name}`
}
}
typeof XueDao // => 'function'
const xfq = new XueDao('徐扶墙', 17) // => {wife: '南宫仆射', name: '徐扶墙', age: 17}
xfq.rank('二') // => '天下第二徐扶墙'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const xfq = new (class XueDao {
constructor(name) {
this.name = name
return {}
}
})('徐扶墙') // => {}
// 定义了 constructor 返回值为空对象
xfq instanceof XueDao // => false
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 2. static
- 静态属性/方法(定义在类上)。
class XueDao {
static daughter = '柿子'
constructor(name, age) {
this.name = name
this.age = age
}
static rank(ranking) {
return `天下第${ranking}徐扶墙`
}
}
// 与 static 等价
XueDao.weapons = '双刀绣冬春雷'
XueDao.daughter // => '柿子'
XueDao.rank('二') // => '天下第二徐扶墙'
XueDao.weapons // => '双刀绣冬春雷'
const xfq = new XueDao('徐扶墙', 17) // => {name: '徐扶墙', age: 17}
xfq.daughter // => undefined
xfq.rank // => undefined
xfq.weapons // => undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 3. #
- 私有属性/方法(只能在类内部访问)。
class XueDao {
#daughter = '柿子'
constructor(name, age) {
this.name = name
this.age = age
}
#rank(ranking) {
return `天下第${ranking}${this.name}`
}
versailles() {
return `${this.#rank('二')} -> ${this.#daughter}`
}
}
XueDao.#daughter // => Uncaught SyntaxError
XueDao.#rank() // => Uncaught SyntaxError
const xfq = new XueDao('徐扶墙', 17) // => {name: '徐扶墙', age: 17, #rank: ƒ, #daughter: '柿子'}
xfq.#daughter // => Uncaught SyntaxError
xfq.#rank() // => Uncaught SyntaxError
xfq.versailles() // => '天下第二徐扶墙 -> 柿子'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 4. extends
通过 extends 关键字实现继承,要比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
子类必须在 constructor 方法中调用 super 方法,否则新建实例时会报错。这是因为子类自己的 this 对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用 super 方法,子类就得不到 this 对象,必须在 super 之后才能使用 this。
super() 作为函数只能在子类 constructor 中使用,内部 this 指向子类实例。super 作为对象调用,在普通方法中,指向父类的原型对象;在静态方法中,指向父类(super - 阮一峰 (opens new window))。
class XueDao {
daughter = '太监总管'
constructor(name, age) {
this.name = name
this.age = age
}
show() {
return this.daughter
}
}
class BeiLiang extends XueDao {
daughter = '柿子'
constructor(name, age, wife) {
super(name, age)
this.wife = wife
}
versailles() {
return super.show()
}
}
const xfq = new BeiLiang('徐扶墙', 17, '南宫仆射') // => {name: '徐扶墙', age: 17, wife: '南宫仆射'}
xfq.versailles() // => '柿子'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 5. Mixin
- 混合继承。
function mix(...mixins) {
class Mix {
constructor(...args) {
for (const mixin of mixins) {
copyProperties(this, new mixin(...args), true) // 拷贝实例属性
}
}
}
for (const mixin of mixins) {
copyProperties(Mix, mixin) // 拷贝静态属性
copyProperties(Mix.prototype, mixin.prototype) // 拷贝原型属性
}
return Mix
}
function copyProperties(target, source, allowName = false) {
const excludeKeys = ['constructor', 'prototype', 'name']
if (allowName) excludeKeys.pop()
for (const key of Reflect.ownKeys(source)) {
if (!excludeKeys.includes(key)) {
const desc = Object.getOwnPropertyDescriptor(source, key)
Object.defineProperty(target, key, desc)
}
}
}
class Foo {
constructor(name, age) {
this.name = name
this.age = age
}
}
class Bar {
daughter = '柿子'
constructor(wife) {
this.wife = wife
}
versailles() {
return this.daughter
}
}
class XueDao extends mix(Foo, Bar) {
rank(ranking) {
return `天下第${ranking}${this.name}`
}
}
const xfq = new XueDao('徐扶墙', 17, '南宫仆射') // => {name: '徐扶墙', age: 17, daughter: '柿子', wife: '徐扶墙'}
xfq.rank('二') // => '天下第二徐扶墙'
xfq.versailles() // => '柿子'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 6. @
- 装饰器只能用于类,不能用于函数,因为存在函数提升(提案阶段,可搭配 babel 使用,目前很多库都已使用)。
import { decoratorClass, readonly, debounce } from 'decorator'
// 装饰类
@decoratorClass
class XueDao {
// 装饰属性
@readonly wife = '南宫仆射'
// 装饰方法
@debounce(1000)
search() {}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
export const decoratorClass = _class => {
_class.age = 17
}
export const readonly = (target, name, descriptor) => {
descriptor.writable = false
return descriptor
}
export const debounce = wait => {
const debounceFn = (fn, ms = 500) => {
let timeoutId
return function(...args) {
clearTimeout(timeoutId)
timeoutId = setTimeout(() => fn.apply(this, args), ms)
}
}
return function(target, name, descriptor) {
descriptor.value = debounceFn(descriptor.value, wait)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21