原型模式

原型模式是在许多相同类型的对象之间共享属性的有用方式。原型是 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')
1
2
3
4
5
6
7
8
9
10
11
12
13

注意这里如何constructor包含一个name属性,而类本身包含一个bark属性。当使用 ES6 类时,在类本身上定义的所有属性,bark在这种情况下,都会自动添加到prototype.

我们可以prototype通过访问prototype构造函数上的属性或通过__proto__任何_实例_上的属性直接看到。

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

console.log(dog1.__proto__)
// constructor: ƒ Dog(name, breed) bark: ƒ bark()
1
2
3
4
5

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

流动

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

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

说我们的狗不仅应该会叫,还应该会玩!我们可以通过向play原型添加属性来实现这一点。

原型链一词表明可能有不止一个步骤。的确!到目前为止,我们只看到了如何访问在第一个__proto__具有引用的对象上直接可用的属性。然而,原型本身也有一个__proto__对象!

让我们创造另一种狗,超级狗!这只狗应该继承了普通狗的一切Dog,但它也应该会飞。我们可以通过扩展Dog类并添加fly方法来创建超级狗。

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

  fly() {
    return 'Flying!'
  }
}
1
2
3
4
5
6
7
8
9

让我们创建一个名为 的会飞的狗Daisy,让她叫起来飞!

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

流动

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


Object.create

Object.create方法允许我们创建一个新对象,我们可以将其原型的值显式传递给该对象。

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

const pet1 = Object.create(dog)
1
2
3
4
5
6
7

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

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


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


参考