原型模式是在同一类型的许多对象之间共享属性的有用方法。原型是 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 就会沿着原型链向下查找该属性在原型链中是否可用。
当处理应该有权访问相同属性的对象时,原型模式非常强大。我们可以简单地将属性添加到原型中,而不是每次都创建属性的副本,因为所有实例都可以访问原型对象。
由于所有实例都可以访问原型,因此即使在创建实例之后也可以轻松向原型添加属性。
说我们的狗不仅应该会叫,还应该会玩,可以通过向原型添加 play
属性来实现这一点。
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");
Dog.prototype.play = () => console.log("Playing now!");
dog1.play();
原型链这一术语表明可以有多个步骤。到目前为止,我们只了解了如何访问 __proto__
引用的第一个对象上直接可用的属性。然而,原型本身也有一个 __proto__
对象。
让我们创造另一种狗,一只超级狗。这只狗应该继承普通 Dog
的一切,但它也应该能够飞。我们可以通过扩展 Dog
类并添加 fly
方法来创建超级狗。
class Dog {
constructor(name) {
this.name = name;
}
bark() {
console.log("Woof!");
}
}
class SuperDog extends Dog {
constructor(name) {
super(name);
}
fly() {
console.log(`Flying!`);
}
}
const dog1 = new SuperDog("Daisy");
dog1.bark();
dog1.fly();
当我们扩展 Dog
类时,我们可以访问 bark
方法。 SuperDog
原型上 __proto__
的值指向 Dog.prototype
对象。
这也就能很清楚认识到为什么它被称为原型链:当我们尝试访问对象上不直接可用的属性时,JavaScript 会递归地遍历 __proto__
指向的所有对象,直到找到该属性。
Object.create
Object.create
方法允许我们创建一个新对象,我们可以显式地将其原型值传递给该对象。
const dog = {
bark() {
return `Woof!`;
},
};
const pet1 = Object.create(dog);
pet1.bark(); // Woof!
console.log("Direct properties on pet1: ", Object.keys(pet1));
console.log("Properties on pet1's prototype: ", Object.keys(pet1.__proto__));
虽然 pet1
本身没有任何属性,但它确实可以访问其原型链上的属性。由于我们将 dog
对象作为 pet1
的原型传递,因此我们可以访问 bark
属性。
Object.create
是一种简单的方法,通过指定新创建的对象的原型,让对象直接从其他对象继承属性。新对象可以通过沿着原型链向下访问新属性。
原型模式允许我们轻松地让对象访问并继承其他对象的属性。由于原型链允许我们访问对象本身没有直接定义的属性,因此我们可以避免方法和属性的重复,从而减少内存使用量。