目录

⚽️ JavaScript 类的基本语法

JavaScript 中,要创建许多相同类型的对象,例如用户(users)、商品(goods)或者任何其他东西。

构造器 和 操作符 new ,可以实现这种需求。

但在现代 JavaScript 中,还有一个更高级的「类(class)」构造方式,它引入许多非常棒的新功能,这些功能对于面向对象编程很有用。

# 基本语法

创建一个类的语法

class myClass {
  constructor() { ... }
  
  method1() { ... }
  method2() { ... }
  method3() { ... }
}
1
2
3
4
5
6
7

然后使用 new MyClass() 来创建具有上述列出的所有方法的新对象。 new 会自动调用 constructor() 方法,因此可以在 constructor() 中初始化对象。

🌰 例子 / 使用例子。:

class User {

  constructor(name) {
    this.name = name;
  }

  sayHi() {
    alert(this.name);
  }

}
1
2
3
4
5
6
7
8
9
10
11
let user = new User("John");
user.sayHi();
1
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
1
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
1
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();
1
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 ... inclass 方法不会出现。

  • 类总是使用 use strict 。 在类构造中的所有代码都将自动进入严格模式。

# 类的表达式

与函数表达式一样,类可以在另外一个表达式中被定义,被传递,被返回,被赋值等。

🌰 例子:

let User = class {
  sayHi() {
    alert("Hello");
  }
};
1
2
3
4
5

🌰 例子 / 类似于命名函数表达式,类表达式也可以有一个名字:

let User = class MyClass {
  sayHi() {
    alert(MyClass); // MyClass 这个名字仅在类内部可见
  }
};

new User().sayHi();
console.log(MyClass) // 外部不可见
1
2
3
4
5
6
7
8

🌰 例子 / 动态创建类,函数的返回值是一个类:

function makeClass(phrase) {
  // 声明一个类并返回它
  return class {
    sayHi() {
      alert(phrase);
    }
  };
}

// 创建一个新的类
let User = makeClass("Hello");

new User().sayHi(); 
1
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
1
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
1
2
3
4
5
6
7

# 类的字段

类的字段是一种允许添加任何属性的语法。只需在表达式中写 =

🌰 例子 / 向类添加一个属性:

class User {
  name = "John";

  sayHi() {
    console.log(`Hello, ${this.name}!`);
  }
}

new User().sayHi();
1
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
1
2
3
4
5
6
7

🌰 例子 / 在赋值时使用更复杂的表达式和函数调用:

class User {
  name = prompt("Name, please?", "John");
}

let user = new User();
console.log(user.name); // John
1
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
1
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);
1
2
3
4
5
6
7
8
9
10
11

类字段 click = () => {...}基于每一个对象 被创建的,在这里对于每一个 Button 对象都有一个独立的方法,在内部都有一个指向此对象的 this

所以可以把 button.click 传递到任何地方,而且 this 的值总是正确的。

📢 上次更新: 2022/09/02, 10:18:16