ES6 Class 特点
- 仅仅是构造函数的语法糖
- 不会 Hoisted
- First Class Citizen,即类可以被当成参数使用
- Class Body 永远都只在 Strict Mode(
'use strict';
)下执行
类表达式 vs 类声明
与函数一样,类的定义也有两种方式:
- 类表达式(Class Expression)
- 类声明(Class Declaration)
但与函数不同的是:类声明不会 Hoisted!
JavaScript
1// 类声明
2class User {
3}
4
5// 类表达式
6const Guest = class {};
语法糖
JavaScript
1class User {
2 constructor(name, age) {
3 this.name = name;
4 this.age = age;
5 }
6
7 // 方法是被加入到 `.prototype` 原型对象中
8 sayHi() {
9 console.log(`你好,${this.name}`);
10 }
11}
12
13// 证明 ES6 Class 仅仅是构造函数的语法糖
14User.prototype.showAge = function () {
15 console.log(`年龄:${this.age}`);
16};
17
18const jason = new User('Jason', 25);
19jason.sayHi(); // 你好,Jason
20jason.showAge(); // 年龄:25
21
22// 检查原型链
23console.dir(jason);
Getter 和 Setter
约定俗成:当 Getter/Setter 中的属性名与字段同名时,加上 _
前缀作为区分。
JavaScript
1class User {
2 constructor(name, age) {
3 // 这里定义的变量叫字段(field)
4 this.name = name;
5 this.age = age;
6 }
7
8 // 这里定义的变量叫属性(property)
9 set age(age) {
10 if (age >= 0 && age <= 100) {
11 // 当属性和字段同名时,加上 `_` 前缀,并定义相应 Getter 方法
12 this._age = age;
13 } else {
14 throw new Error(`${age}不在0~100的年龄范围内`)
15 }
16 }
17
18 get age() {
19 return this._age;
20 }
21}
22
23const jason = new User('Jason', 25);
24console.log(jason.age); // 25
25
26jason.age = 99;
27console.log(jason.age); // 99
静态属性和方法
JavaScript
1class User {
2 constructor(name, age) {
3 this.name = name;
4 this.age = age;
5 }
6
7 // 方式一(推荐)
8 static versionPrefix = 'v';
9
10 static info () {
11 console.log(`版本:${User.versionPrefix}${User.versionNum}`);
12 }
13}
14
15// 方式二
16User.versionNum = '1.0.0';
17
18const jason = new User('Jason', 25);
19
20console.log(jason.versionNum); // undefined
21
22console.log(`${User.versionPrefix}${User.versionNum}`); // v1.0.0
23User.info(); // 版本:v1.0.0
继承与多态
JavaScript
1class User {
2 constructor(name, age) {
3 this.name = name;
4 this.age = age;
5 };
6
7 sayHi() {
8 console.log(`你好,${this.name}`);
9 }
10}
11
12class Vip extends User {
13 constructor(name, age, level) {
14 // 必须位于构造方法的第一句
15 super(name, age);
16
17 this.level = level;
18 };
19
20 sayHi() {
21 console.log(`用户:${this.name},VIP 等级:${this.level}`);
22 }
23}
24
25const jason = new Vip('Jason', 25, '3');
26
27jason.sayHi(); // 用户:Jason,VIP 等级:3
28
29// 检查原型链
30console.log(jason instanceof Vip); // true
31console.log(jason instanceof User); // true
32console.log(jason instanceof Object); // true
封装
伪私有属性和方法
因很长一段时间 JS 都是没有私有特性,故约定俗成:以 _
开头的属性或方法就是私有的,其他开发人员不应直接操作这类名称的变量。
JavaScript
1class User {
2 constructor(name, passwd) {
3 // 受保护的属性
4 this._rpasswd = '123';
5
6 this.name = name;
7 this.passwd = passwd;
8 };
9
10 login() {
11 if (this._check()) return console.log(`${this.name},登录成功`);
12
13 console.log('用户名或密码错误');
14 }
15
16 // 受保护的方法
17 _check() {
18 return this.passwd === this._rpasswd;
19 }
20}
21
22const jason = new User('Jason', '123');
23jason.login(); // Jason,登录成功
24
25// 因为不是语言特性,所以仍可访问,但此时开发者自身是知道执行了不合规的操作
26console.log(jason._rpasswd); // 123
27console.log(jason._check()); // true
真私有字段和方法
Class field declarations for JavaScript 中为 JS 引入了私有字段和方法(同样适用于静态)。
JavaScript
1class User {
2 // 1) Public fields
3 name;
4 passwd;
5
6 // 2) Private field
7 #rPasswd = '123';
8
9 constructor(name, passwd) {
10 this.name = name;
11 this.passwd = passwd;
12 };
13
14 // 3) Public method
15 login() {
16 if (this.#check()) return console.log(`${this.name},登录成功`);
17
18 console.log('用户名或密码错误');
19 }
20
21 // 4) private method
22 #check() {
23 return this.passwd === this.#rPasswd;
24 }
25}
26
27const jason = new User('Jason', '123');
28jason.login(); // Jason,登录成功
29
30// 因为是语言特性,所以真正实现了私有
31// console.log(jason.#rPasswd); // Uncaught SyntaxError
32// console.log(jason.#check()); // Uncaught SyntaxError
链式调用
JavaScript
1class Account {
2 constructor() {
3 this._movements = [];
4 }
5
6 deposit(val) {
7 this.movements.push(val);
8 return this; // 返回对象本身
9 }
10
11 withdraw(val) {
12 this.deposit(-val);
13 return this; // 返回对象本身
14 }
15
16 get movements() {
17 return this._movements;
18 }
19}
20
21const account = new Account();
22
23account
24 .deposit(100)
25 .withdraw(50)
26 .withdraw(20)
27 .deposit(70);
28
29console.log(account.movements); // [100, -50, -20, 70]