有兴趣阅读我们的下一本书吗?了解更多关于 使用 React 构建大型 JavaScript Web 应用程序

设计模式

原型模式

原型模式是一种在同类型多个对象之间共享属性的有效方法。原型是 JavaScript 中的一个原生对象,可以通过原型链被对象访问。

在我们的应用程序中,我们经常需要创建许多相同类型的对象。一个有效的方法是创建 ES6 类多个实例。

假设我们要创建很多条狗!在我们的例子中,狗不能做太多事情:它们只有名字,并且可以吠叫!

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

  bark() {
    return `Woof!`;
  }
}

const dog1 = new Dog("Daisy");
const dog2 = new Dog("Max");
const dog3 = new Dog("Spot");

注意这里 constructor 包含 name 属性,类本身包含 bark 属性。在使用 ES6 类时,所有在类本身定义的属性(在本例中为 bark)都会自动添加到 prototype 中。

我们可以通过访问构造函数的 prototype 属性,或通过访问任何实例的 __proto__ 属性来直接查看 prototype

console.log(Dog.prototype);
// constructor: ƒ Dog(name, breed) bark: ƒ bark()

console.log(dog1.__proto__);
// constructor: ƒ Dog(name, breed) bark: ƒ bark()

构造函数任何实例的 __proto__ 值,都是指向构造函数原型的直接引用!当我们尝试访问对象上不存在的属性时,JavaScript 将沿着原型链查找,看该属性是否在原型链中。

Flow

原型模式在处理应该访问相同属性的对象时非常强大。与其每次都创建属性的副本,不如简单地将属性添加到原型中,因为所有实例都可以访问原型对象。

由于所有实例都可以访问原型,因此即使在创建实例之后,也可以轻松地向原型添加属性。

假设我们的狗不仅能吠叫,还能玩耍!我们可以通过向原型添加 play 属性来实现这一点。

index.js
1class Dog {
2 constructor(name) {
3 this.name = name;
4 }
5
6 bark() {
7 return `Woof!`;
8 }
9}
10
11const dog1 = new Dog("Daisy");
12const dog2 = new Dog("Max");
13const dog3 = new Dog("Spot");
14
15Dog.prototype.play = () => console.log("Playing now!");
16
17dog1.play();

术语 原型链 表示可能存在不止一个步骤。确实如此!到目前为止,我们只看到了如何访问 __proto__ 直接引用的第一个对象上的属性。但是,原型本身也拥有一个 __proto__ 对象!

让我们创建另一种类型的狗,超级狗!这种狗应该继承普通 Dog 的所有特性,但它还应该能飞。我们可以通过扩展 Dog 类并添加一个 fly 方法来创建超级狗。

class SuperDog extends Dog {
  constructor(name) {
    super(name);
  }

  fly() {
    return "Flying!";
  }
}

让我们创建一个名为 Daisy 的飞翔的狗,让她吠叫和飞翔!

index.js
1class Dog {
2 constructor(name) {
3 this.name = name;
4 }
5
6 bark() {
7 console.log("Woof!");
8 }
9}
10
11class SuperDog extends Dog {
12 constructor(name) {
13 super(name);
14 }
15
16 fly() {
17 console.log(`Flying!`);
18 }
19}
20
21const dog1 = new SuperDog("Daisy");
22dog1.bark();
23dog1.fly();

我们可以访问 bark 方法,因为我们扩展了 Dog 类。SuperDog 原型的 __proto__ 值指向 Dog.prototype 对象!

Flow

很明显,为什么它被称为原型:当我们尝试访问对象上不存在的属性时,JavaScript 会递归地遍历 __proto__ 指向的所有对象,直到找到该属性!


Object.create

Object.create 方法允许我们创建一个新对象,我们可以明确地传递其原型的值。

const dog = {
  bark() {
    return `Woof!`;
  },
};

const pet1 = Object.create(dog);

虽然 pet1 本身没有任何属性,但它可以访问其原型链上的属性!因为我们传递了 dog 对象作为 pet1 的原型,所以我们可以访问 bark 属性。

index.js
1const dog = {
2 bark() {
3 console.log(`Woof!`);
4 }
5};
6
7const pet1 = Object.create(dog);
8
9pet1.bark(); // Woof!
10console.log("Direct properties on pet1: ", Object.keys(pet1));
11console.log("Properties on pet1's prototype: ", Object.keys(pet1.__proto__));

完美!Object.create 是一种简单的方法,让对象可以直接从其他对象继承属性,方法是指定新创建对象的原型。新对象可以通过沿着原型链查找来访问新属性。


原型模式允许我们轻松地让对象访问和继承其他对象的属性。由于原型链允许我们访问对象本身未直接定义的属性,因此我们可以避免方法和属性的重复,从而减少内存使用量。


参考资料