原型模式
原型模式是在许多相同类型的对象之间共享属性的实用方式。原型是 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
属性或通过任何实例的 __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 SuperDog extends Dog {
constructor(name) {
super(name);
}
fly() {
return "Flying!";
}
}
让我们创建一只叫 Daisy
的会飞的狗,并让她吠叫和飞翔吧!
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();
我们可以访问 bark
方法,因为我们继承了 Dog
类。SuperDog
原型上的 __proto__
值指向 Dog.prototype
对象!
很清楚为什么它被称作原型链:当我们尝试访问对象上不直接可用的属性时,JavaScript 会递归地遍历 __proto__
指向的所有对象,直到找到该属性!
Object.create
Object.create
方法允许我们创建一个新对象,我们可以将其原型的值显式传递给该对象。
const dog = {
bark() {
return `Woof!`;
}
};
const pet1 = Object.create(dog);
虽然 pet1
本身没有任何属性,但它确实可以访问其原型链上的属性!由于我们将 dog
对象作为 pet1
的原型传递,我们可以访问 bark
属性。
const dog = {
bark() {
console.log(`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__));
完美! Object.create
是一种通过指定新创建对象的原型让对象直接从其他对象继承属性的简单方法。新对象可以通过原型链访问新属性。
原型模式允许我们轻松地让对象访问和继承其他对象的属性。由于原型链允许我们访问不是直接在对象本身上定义的属性,我们可以避免方法和属性的重复,从而减少使用的内存量。