ES6 Class

ES6引入了Class(类)作为对象的模板,通过class关键字,可以定义类

ES6的class可以基本看作只是一个语法糖,它的绝大部分功能,ES5都可以做到

ES6的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已

1
2
3
4
5
//定义类
class Person {
constructor () {}
run () {}
}

TS Class

定义类

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
interface DomCls {
createElement(el:string):HTMLElement
setText(el:HTMLElement,text:string|null):void
render(data:Vnode):HTMLElement
}

interface Vnode {
tag:string
text?:string
children?:Vnode[]
}

interface VueCls {
options:Options
init(): void
}

interface Options {
el: string|HTMLElement
}

// 虚拟DOM类,实现DomCls接口
class Dom implements DomCls {
// 创建节点的方法
createElement(el:string) {
return document.createElement(el)
}
// 填充文本的方法
setText(el:HTMLElement,text:string|null) {
el.textContent=text
}
// 渲染节点的函数
render(data:Vnode) {
// 根节点
let root = this.createElement(data.tag)
// 如果有子节点,渲染子节点
if(data.children&&Array.isArray(data.children)) {
data.children.forEach(item=> {
// 处理下一层
let child = this.render(item)
// 给本层的父节点添加下一层(子节点)
root.appendChild(child)
})
// 如果没有子节点,填充文字
} else {
this.setText(root,data.text)
}
// 返回本层的根节点
return root
}
}

// Vue类,继承Dom类,实现VueCls接口
class Vue extends Dom implements VueCls {
// 属性,可以有默认值
options: Options = {el:"#app"}

// 构造函数
constructor (options?: Options) {
// super()是父类的构造函数
super()
options? this.options=options: null
this.init()
}

// 方法
init(): void {
// 虚拟DOM就是通过JS去描述并渲染真实DOM
let data:Vnode = {
tag:"div",
children:[
{
tag:"section",
text:"子节点1"
},
{
tag:"p",
text:"子节点2"
}
]
}

// 获取应用根节点
let app =
typeof(this.options.el) === "string"
? document.querySelector(this.options.el)
: this.options.el
// 把内容都挂载到应用根节点上
app.appendChild(this.render(data))
}
}

new Vue({
el:"#app"
})

修饰符

readonly

表示只读,只能应用在索引签名或属性上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Vue extends Dom implements VueCls {
readonly options: Options = {el:"#app"}

constructor (options?: Options) {
super()
options? this.options=options: null
this.init()
}

init(): void {
// ...省略
}
}

const v = new Vue({
el:"#app"
})
// 不能为options赋值,因为它是只读的
// v.options = {}

private

表示私有,只能在这个类的内部使用,外部甚至子类都不能调用

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
class Dom {
private createElement(el:string) {
return document.createElement(el)
}
private setText(el:HTMLElement,text:string|null) {
el.textContent=text
}
private render(data:Vnode) {
// 可以调用createElement方法,因为现在位于Dom类内部,而且这是Dom类自己的方法
let root = this.createElement(data.tag)
if(data.children&&Array.isArray(data.children)) {
data.children.forEach(item=> {
let child = this.render(item)
root.appendChild(child)
})
} else {
// 可以调用setText方法,因为现在位于Dom类内部,而且这是Dom类自己的方法
this.setText(root,data.text)
}
return root
}
}

class Vue extends Dom implements VueCls {
options: Options = {el:"#app"}

constructor (options?: Options) {
super()
options? this.options=options: null
this.init()
}

init(): void {
let data:Vnode = {
// ...省略
}

let app =
typeof(this.options.el) === "string"
? document.querySelector(this.options.el)
: this.options.el
// 不能调用render方法,因为它是Dom类的私有方法
app.appendChild(this.render(data))
}
}

const dom = new Dom()
// 不能调用createElement方法,因为这里不是Dom类内部
dom.createElement("#yajue")

protect

表示受保护,可以在这个类以及子类的内部使用,外部不能调用

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
55
class Dom {
protected createElement(el:string) {
return document.createElement(el)
}
protected setText(el:HTMLElement,text:string|null) {
el.textContent=text
}
protected render(data:Vnode) {
// 可以调用createElement方法,因为现在位于Dom类内部,而且这是Dom类自己的方法
let root = this.createElement(data.tag)
if(data.children&&Array.isArray(data.children)) {
data.children.forEach(item=> {
let child = this.render(item)
root.appendChild(child)
})
} else {
// 可以调用setText方法,因为现在位于Dom类内部,而且这是Dom类自己的方法
this.setText(root,data.text)
}
return root
}
}

class Vue extends Dom implements VueCls {
options: Options = {el:"#app"}

constructor (options?: Options) {
super()
options? this.options=options: null
this.init()
}

init(): void {
let data:Vnode = {
// ...省略
}

let app =
typeof(this.options.el) === "string"
? document.querySelector(this.options.el)
: this.options.el
// 可以调用render方法,因为它是Dom类的受保护方法,且现在位于Dom类的子类Vue类内部
app.appendChild(this.render(data))
}
}

const dom = new Dom()
// 不能调用createElement方法,因为这里不是Dom类或Dom的子类内部
dom.createElement("#yajue")

const v = new Vue({
el:"#app"
})
// 不能调用createElement方法,因为这里不是Dom类或Dom的子类内部
v.createElement("#yajue")

public

表示公用,任何地方都可以调用

所有的属性和方法,如果不加修饰符,都默认为public

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
55
class Dom {
createElement(el:string) {
return document.createElement(el)
}
public setText(el:HTMLElement,text:string|null) {
el.textContent=text
}
render(data:Vnode) {
// 可以调用createElement方法,因为它是公用方法
let root = this.createElement(data.tag)
if(data.children&&Array.isArray(data.children)) {
data.children.forEach(item=> {
let child = this.render(item)
root.appendChild(child)
})
} else {
// 可以调用setText方法,因为它是公用方法
this.setText(root,data.text)
}
return root
}
}

class Vue extends Dom implements VueCls {
options: Options = {el:"#app"}

constructor (options?: Options) {
super()
options? this.options=options: null
this.init()
}

init(): void {
let data:Vnode = {
// ...省略
}

let app =
typeof(this.options.el) === "string"
? document.querySelector(this.options.el)
: this.options.el
// 可以调用render方法,因为它是公用方法
app.appendChild(this.render(data))
}
}

const dom = new Dom()
// 可以调用createElement方法,因为它是公用方法
dom.createElement("#yajue")

const v = new Vue({
el:"#app"
})
// 可以调用setText方法,因为它是公用方法
v.setText("#yajue", "114514")

super

在子类中,super就代表父类,所以super()代表父类的构造函数(将this指向改为了子类),super.xxx可以取到父类的属性或方法

静态属性/方法

例如Promise.all,静态方法直接通过类名调用

静态方法中只能调用静态属性和方法,普通方法只能调用普通属性和方法

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
class Vue extends Dom implements VueCls {
options: Options = {el:"#app"}
// 静态属性
static a = "12321"

constructor (options?: Options) {
super()
options? this.options=options: null
this.init()
}

init(): void {
let data:Vnode = {
// ...省略
}

let app =
typeof(this.options.el) === "string"
? document.querySelector(this.options.el)
: this.options.el
app.appendChild(this.render(data))
}

// 静态方法
static version():string {
// 不能调用
// console.log(this.options)
// this.init()

// 静态方法中只能调用静态属性和方法
console.log(this.a)
console.log(this.abc())

return "v 1.1.4"
}

static abc():string {
return "???"
}
}

console.log(Vue.a) // ->"12321"
console.log(Vue.version()) // ->"1.1.4"

const v = new Vue({
el:"#app"
})
// 调用不了
v.a
v.version()

get和set

读取或设置属性时会触发get或set函数,格式为get/set [属性]() {}

可以用get和set函数做属性的拦截器

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
class Ref {
private _value:any

// get和set帮你声明好属性了,不要重复声明
// value:any

constructor (value:any) {
this._value = value
}

// 读取value时触发get函数
get value() {
console.log("我被读取了")
return this._value
}

// 设置value时触发set函数
set value(newVal:any) {
console.log("我被修改了")
this._value = newVal
}
}

const r = new Ref(5)
console.log(r.value)
r.value = 6

抽象类

通过abstract修饰符定义的类就是抽象类,又称基类;抽象类中通过abstract修饰符定义的方法就是抽象方法

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
// 抽象类
abstract class Vue {
name:string
constructor(name?:string) {
this.name = name
}

// 抽象类里的非抽象方法可以实现(和接口implement的区别)
getName():string {
return this.name
}

// 抽象类里的抽象方法只能描述,不能实现
// abstract init() {}
abstract init(name: string):void
}
// 不能创建抽象类的实例
// new Vue()

// 抽象类主要用于创建派生类(继承类)
class React extends Vue {

constructor() {
super("yjsp")
}

// 派生类必须实现抽象类的抽象方法
init(omg:string) {
console.log(omg)
}

// 在抽象类定义的属性和方法也可以用于派生类
setName(name:string) {
this.name = name
}
}
// 派生类可以被实例化
const react = new React()
react.init("omg")
console.log(react.getName())
react.setName("yajue")
console.log(react.getName())