JavaScript 中的类

0 股票
0
0
0
0

介绍

类是创建对象的模板。它们将数据与操作这些数据的代码封装在一起。JavaScript 中的类基于原型构建,但它们也具有一些类独有的语法和语义。.

类本质上是“特殊函数”,就像你可以定义函数表达式和函数声明一样,类也可以用两种方式定义:类表达式或类声明。.

// Declaration
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
// Expression; the class is anonymous but assigned to a variable
const Rectangle = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
// Expression; the class has its own name
const Rectangle = class Rectangle2 {
constructor(height, width) {
this.height = height;
this.width = width;
}
};

与函数声明类似,类声明也可以是匿名的,或者使用与被赋值的变量不同的名称。但是,与函数声明不同的是,类声明与 `let` 或 `const` 一样,都存在时间限制,并且其行为如同这些限制未被解除一样。.

班级

类的主体部分是用花括号 {} 括起来的部分。在这里,你可以定义类的成员,例如方法或构造函数。.

即使没有“use strict”指令,类的主体也会以严格模式执行。.

类元素可以从三个方面来描述:

  • 类型:接收器、调节器、方法或现场
  • 位置:静态或采样
  • 可见性:公开或私密

构造函数

构造函数方法是用于创建和初始化类对象的一种特殊方法。一个类中只能有一个名为“constructor”的特殊方法——如果类中包含多个名为“constructor”的构造函数,则会抛出语法错误。.

构造函数可以使用 super 关键字来调用父类的构造函数。.

您可以在构造函数内部创建实例属性:

class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}

或者,如果实例属性的值不依赖于构造函数参数,则可以将它们定义为类字段。.

静态初始化块

静态块允许灵活地初始化静态属性,包括在初始化期间评估表达式,同时仍然允许访问私有作用域。.

可以声明多个静态块,这些块可以与静态字段和方法的声明穿插在一起(所有静态项都按声明的顺序进行评估)。.

方法

方法定义在类的每个实例的原型上,并被所有实例共享。方法可以是简单函数、异步函数、生成器函数或异步生成器函数。更多信息,请参阅方法定义。.

class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
// Getter
get area() {
return this.calcArea();
}
// Method
calcArea() {
return this.height * this.width;
}
*getSides() {
yield this.height;
yield this.width;
yield this.height;
yield this.width;
}
}
const square = new Rectangle(10, 10);
console.log(square.area); // 100
console.log([...square.getSides()]); // [10, 10, 10, 10]

静态方法和字段

`static` 关键字用于定义类的静态方法或字段。静态属性(字段和方法)定义在类本身,而不是每个实例上。静态方法通常用于创建应用程序的实用函数,而静态字段则适用于缓存、固定配置或任何其他无需在实例间重复存储的数据。.

class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
static displayName = "Point";
static distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.hypot(dx, dy);
}
}
const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
p1.displayName; // undefined
p1.distance; // undefined
p2.displayName; // undefined
p2.distance; // undefined
console.log(Point.displayName); // "Point"
console.log(Point.distance(p1, p2)); // 7.0710678118654755

现场公告

使用类字段声明语法,构造函数示例可以写成如下形式:

class Rectangle {
height = 0;
width;
constructor(height, width) {
this.height = height;
this.width = width;
}
}

类字段类似于对象属性,而不是变量,因此我们不能使用像 `const` 这样的关键字来声明它们。在 JavaScript 中,私有属性使用特殊的标识符语法,因此也不能使用像 `public` 和 `private` 这样的修饰关键字。.

如上所示,字段可以声明默认值,也可以不声明默认值。默认情况下,不带默认值的字段不会被定义。通过预先声明字段,类定义更加清晰易懂,并且字段始终存在,这有助于优化。.

有关更多信息,请参阅公共类字段。.

私人财产

使用私有字段,可以按如下方式修改定义。.

class Rectangle {
#height = 0;
#width;
constructor(height, width) {
this.#height = height;
this.#width = width;
}
}

从类外部引用私有字段是错误的。私有字段只能在类内部进行读写操作。通过定义类外部不可见的内容,可以确保类的用户不会依赖于可能随版本变化的内部实现。.

私有字段只能在字段声明中预先声明。它们不能像普通属性那样通过赋值来创建。.

请参阅“私有财产”了解更多信息。.

遗产

extends 关键字用于类声明或类表达式中,以创建一个类作为另一个构造函数(类或函数)的子类。.

class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
constructor(name) {
super(name); // call the super class constructor and pass in the name parameter
}
speak() {
console.log(`${this.name} barks.`);
}
}
const d = new Dog("Mitzie");
d.speak(); // Mitzie barks.

如果子类中有构造函数,则必须先调用 `super()` 才能使用它。`super` 关键字也可用于调用与父类相关的方法。.

class Cat {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Lion extends Cat {
speak() {
super.speak();
console.log(`${this.name} roars.`);
}
}
const l = new Lion("Fuzzy");
l.speak();
// Fuzzy makes a noise.
// Fuzzy roars.

评估令

当对类声明或类表达式求值时,其各个组成部分按以下顺序求值:

  1. 首先评估扩展子句(如果有)。该子句必须评估为有效的构造函数或 null,否则将抛出 TypeError 异常。.
  2. 构造函数方法会被提取出来,如果构造函数不存在,则会替换为默认实现。然而,由于构造函数定义本质上只是一个方法定义,因此这一步骤是不可见的。.
  3. 类元素的属性键按声明顺序进行求值。如果属性键被求值,则使用该属性值计算的表达式的值将被设置为包裹该类的表达式的值(而不是类本身的值)。目前还没有任何属性值被求值。.
  4. 方法和访问器按照声明顺序安装。实例方法和访问器安装在当前类的原型属性上,静态方法和访问器安装在类本身上。私有实例方法和访问器保留稍后直接安装在实例上。此步骤不可见。.
  5. 现在,该类已使用扩展指定的原型和构造函数指定的实现进行初始化。对于上述所有步骤,如果求值的表达式尝试访问类名,则会抛出 ReferenceError 异常,因为该类尚未初始化。.
  6. 类元素的值按照声明顺序进行评估:
  • 对于每个实例字段(公共或私有),都会存储其初始化表达式。初始化表达式会在实例创建时、构造函数开始时(对于基类)或 super() 调用返回之前(对于派生类)进行求值。.
  • 对于每个常量字段(公共或私有),都会使用此集合对其进行初始化,并将该字段的值计算到类本身,并在类上创建该属性。.
  • 静态初始化块会根据类本身中的此设置进行求值。.

示例

将此与实例方法和静态方法联系起来

当调用静态方法或实例方法时,如果未指定方法参数(例如,将方法赋值给一个变量后再调用它),则方法内部的值将为 undefined。即使没有使用 `using strict` 指令,也会出现同样的情况,因为类体内部的代码始终以严格模式执行。.

class Animal {
speak() {
return this;
}
static eat() {
return this;
}
}
const obj = new Animal();
obj.speak(); // the Animal object
const speak = obj.speak;
speak(); // undefined
Animal.eat(); // class Animal
const eat = Animal.eat;
eat(); // undefined

如果在非严格模式下,我们使用传统的基于函数的语法重写上述代码,那么对该方法的调用将自动限制为 globalThis。在严格模式下,this 的值将保持未定义状态。.

function Animal() {}
Animal.prototype.speak = function () {
return this;
};
Animal.eat = function () {
return this;
};
const obj = new Animal();
const speak = obj.speak;
speak(); // global object (in non–strict mode)
const eat = Animal.eat;
eat(); // global object (in non-strict mode)
发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

您可能也喜欢