设计模式
原型模式
原型模式是一种在同类型多个对象之间共享属性的有效方法。原型是 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
属性来实现这一点。
1class Dog {2 constructor(name) {3 this.name = name;4 }56 bark() {7 return `Woof!`;8 }9}1011const dog1 = new Dog("Daisy");12const dog2 = new Dog("Max");13const dog3 = new Dog("Spot");1415Dog.prototype.play = () => console.log("Playing now!");1617dog1.play();
术语 原型链 表示可能存在不止一个步骤。确实如此!到目前为止,我们只看到了如何访问 __proto__
直接引用的第一个对象上的属性。但是,原型本身也拥有一个 __proto__
对象!
让我们创建另一种类型的狗,超级狗!这种狗应该继承普通 Dog
的所有特性,但它还应该能飞。我们可以通过扩展 Dog
类并添加一个 fly
方法来创建超级狗。
class SuperDog extends Dog {
constructor(name) {
super(name);
}
fly() {
return "Flying!";
}
}
让我们创建一个名为 Daisy
的飞翔的狗,让她吠叫和飞翔!
1class Dog {2 constructor(name) {3 this.name = name;4 }56 bark() {7 console.log("Woof!");8 }9}1011class SuperDog extends Dog {12 constructor(name) {13 super(name);14 }1516 fly() {17 console.log(`Flying!`);18 }19}2021const dog1 = new SuperDog("Daisy");22dog1.bark();23dog1.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
属性。
1const dog = {2 bark() {3 console.log(`Woof!`);4 }5};67const pet1 = Object.create(dog);89pet1.bark(); // Woof!10console.log("Direct properties on pet1: ", Object.keys(pet1));11console.log("Properties on pet1's prototype: ", Object.keys(pet1.__proto__));
完美!Object.create
是一种简单的方法,让对象可以直接从其他对象继承属性,方法是指定新创建对象的原型。新对象可以通过沿着原型链查找来访问新属性。
原型模式允许我们轻松地让对象访问和继承其他对象的属性。由于原型链允许我们访问对象本身未直接定义的属性,因此我们可以避免方法和属性的重复,从而减少内存使用量。