⚽️ JavaScript 类的基本语法
JavaScript 中,要创建许多相同类型的对象,例如用户(users)、商品(goods)或者任何其他东西。
构造器 和 操作符 new
,可以实现这种需求。
但在现代 JavaScript 中,还有一个更高级的「类(class)」构造方式,它引入许多非常棒的新功能,这些功能对于面向对象编程很有用。
# 基本语法
创建一个类的语法
class myClass {
constructor() { ... }
method1() { ... }
method2() { ... }
method3() { ... }
}
2
3
4
5
6
7
然后使用 new MyClass()
来创建具有上述列出的所有方法的新对象。 new
会自动调用 constructor()
方法,因此可以在 constructor()
中初始化对象。
🌰 例子 / 使用例子。:
class User {
constructor(name) {
this.name = name;
}
sayHi() {
alert(this.name);
}
}
2
3
4
5
6
7
8
9
10
11
let user = new User("John");
user.sayHi();
2
new User("John")
:
- 一个新对象被创建。
constructor
使用给定的参数运行,并将其赋值给this.name
。- 然后就可以调用 对象 中的方法
sayHi()
注意
类的方法之间没有逗号。不要把这里的符号与对象字面量相混淆。在类中,不需要逗号。
# 理解 class
class
的类型:
class User {
constructor(name) { this.name = name; }
sayHi() { alert(this.name); }
}
alert(typeof User); // function
2
3
4
5
6
在 JavaScript 中,类是一种函数。
实际上, class User {...}
构造有以下工作:
- 创建一个名为
User
的函数,该函数称为类 声明 的结果。该函数的代码来自于constructor
方法(如果没有则假定为空)。 - 存储类中的方法,例如
User.prototype
中的sayHi
。
类被创建以后,当调用其方法时,它会从原型中获取对应的方法( 类似 F.prototype
),因此,通过 new User
创建的对象可以 访问类中的方法:
console.log(User = User.prototyope.constructor) // true
cosole.log(User.prototype.sayHi) // 函数
console.log(Object.getOwnPropertyNames(User.prototype)) // constructor, sayHi
2
3
4
5
# 类的使用不仅仅是 语法糖
其实类完成的工作,相当于声明函数,将方法添加到函数,使用 new
创建对象:
// 1. 创建构造器函数
function User(name) {
this.name = name;
}
// 函数的原型(prototype)默认具有 "constructor" 属性,
// 所以,我们不需要创建它
// 2. 将方法添加到原型
User.prototype.sayHi = function() {
alert(this.name);
};
// 用法:
let user = new User("John");
user.sayHi();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
但是 手动创建 与 类创建 的函数 有着重大差异:
通过
class
创建的函数具有特殊的内部属性标记[[IsClassConstructor]]: true
。因此,它与手动创建并不完全相同。class
创建的函数必须要使用new
调用:class User { constructor() {} } console.log(typeof User); // function User(); // Error: Class constructor User cannot be invoked without 'new'
1
2
3
4
5
6大多数 JavaScript 引擎中的 类构造器的字符串 表示形式都以
class…
开头:class User { constructor() {} } console.log(User) // class User { ... }
1
2
3
4
5类的方法 不可枚举。类定义将
"prototype"
中的所有方法的enumerable
标志设置为false
。所以如果对一个对象调用
for ... in
,class
方法不会出现。类总是使用
use strict
。 在类构造中的所有代码都将自动进入严格模式。
# 类的表达式
与函数表达式一样,类可以在另外一个表达式中被定义,被传递,被返回,被赋值等。
🌰 例子:
let User = class {
sayHi() {
alert("Hello");
}
};
2
3
4
5
🌰 例子 / 类似于命名函数表达式,类表达式也可以有一个名字:
let User = class MyClass {
sayHi() {
alert(MyClass); // MyClass 这个名字仅在类内部可见
}
};
new User().sayHi();
console.log(MyClass) // 外部不可见
2
3
4
5
6
7
8
🌰 例子 / 动态创建类,函数的返回值是一个类:
function makeClass(phrase) {
// 声明一个类并返回它
return class {
sayHi() {
alert(phrase);
}
};
}
// 创建一个新的类
let User = makeClass("Hello");
new User().sayHi();
2
3
4
5
6
7
8
9
10
11
12
13
# 类的 getters
/ setters
就像 对象字面量,类可能包括 getters/setters,计算属性等。
🌰 例子:
class User {
constructor(name) {
// 调用 setter
this.name = name;
}
get name() {
return this._name;
}
set name(value) {
if (value.length < 4) {
console.log("Name is too short.");
return;
}
this._name = value;
}
}
let user = new User("John");
console.log(user.name); // getters
user.name = "" // "Name is too short." setters
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
从技术上来讲,这样的类声明可以通过在
User.prototype
中创建 getters 和 setters 来实现。
# 类的计算属性
🌰 例子 / 使用中括号 [...]
的计算方法名:
class User {
['say' + 'Hi']() {
console.log("Hello");
}
}
new User().sayHi(); // Hello
2
3
4
5
6
7
# 类的字段
类的字段是一种允许添加任何属性的语法。只需在表达式中写 =
。
🌰 例子 / 向类添加一个属性:
class User {
name = "John";
sayHi() {
console.log(`Hello, ${this.name}!`);
}
}
new User().sayHi();
2
3
4
5
6
7
8
9
类字段重要的不同之处在于,它们会在每个独立对象中被设好,而不是设在 User.prototype
:
class User {
name = "John";
}
let user = new User();
console.log(user.name); // John
console.log(User.prototype.name); // undefined
2
3
4
5
6
7
🌰 例子 / 在赋值时使用更复杂的表达式和函数调用:
class User {
name = prompt("Name, please?", "John");
}
let user = new User();
console.log(user.name); // John
2
3
4
5
6
# 利用类字段绑定方法
在 函数绑定 中,JavaScript 中的函数具有动态的 this
。它取决于调用上下文。因此,如果一个对象方法被传递到某处,或者在另一个上下文中被调用,则 this
将不再是对其对象的引用。例如:
class Button {
constructor(value) {
this.value = value;
}
click() {
console.log(this.value);
}
}
let button = new Button("hello");
setTimeout(button.click, 1000); // undefined
2
3
4
5
6
7
8
9
10
11
12
13
在
setTimeout
中丢失了this
。
可以通过:
- 传递一个包装函数,例如
setTimeout(() => button.click(), 1000)
。 - 将方法绑定到对象,例如在 constructor 中。
类字段 可以更加优雅地解决:
class Button {
constructor(value) {
this.value = value;
}
click = () => {
console.log(this.value);
}
}
let button = new Button("hello");
setTimeout(button.click, 1000);
2
3
4
5
6
7
8
9
10
11
类字段
click = () => {...}
是 基于每一个对象 被创建的,在这里对于每一个Button
对象都有一个独立的方法,在内部都有一个指向此对象的this
。所以可以把
button.click
传递到任何地方,而且this
的值总是正确的。