原型模式(创建型)
原型模式 —— 谈 Prototype 无小事
原型模式不仅是一种设计模式,还是一种编程范式(programming paradigm),是 JavaScript 面向对象系统实现的根基。
在原型模式下创建对象时,会先找到一个对象作为原型,然后通过克隆原型的方式来创建出一个与原型一样(共享一套数据/方法)的对象。在 JavaScript 中,Object.create 方法是原型模式的天然实现——准确地说,借助 Prototype 来实现对象的创建和原型的继承,就是在应用原型模式。
在 JavaScript 中使用原型模式,并不是为了得到一个副本,而是为了得到与构造函数(类)相对应的类型的实例、实现数据/方法的共享。克隆是实现这个目的的方法,但克隆本身并不是我们的目的。
一、以类或原型为中心的语言
1、Java 中的类
原型模式是 JavaScript 这门语言面向对象系统的根本。
但在 JAVA 中,类才是它面向对象系统的根本。所以 JAVA 可以选择不使用原型模式 —— 这样所有的实例都要从类中来,当创建两个一模一样的实例时,可以这样 ▼
假设实例从 Dog 类中来,必传参数为姓名、性别、年龄和品种:
Dog dog = new Dog('旺财', 'male', 3, '柴犬')
Dog dog_copy = new Dog('旺财', 'male', 3, '柴犬')
可以看到,需要把一模一样的参数传两遍,非常麻烦。
而原型模式可以通过调用克隆方法的方式达到同样的目的,比较方便,所以 Java 专门针对原型模式设计了一套接口和方法,在必要的场景下会通过原型方法来应用原型模式。当然,在更多的情况下,Java 仍以“实例化类”这种方式来创建对象。
2、 JavaScript 中的类
由于 ES6 的类是基于原型继承的语法糖,所以 ES6 Class 类语法不会为 JavaScript 引入新的面向对象的继承模型。
当我们尝试用 class 去定义一个 Dog 类时:
class Dog {
constructor(name, age) {
this.name = name;
this.age = age;
}
eat() {
console.log("肉骨头真好吃");
}
}
其实等价于写了这么一个构造函数:
function Dog(name, age) {
this.name = name;
this.age = age;
}
Dog.prototype.eat = function () {
console.log("肉骨头真好吃");
};
所以说 JavaScript 的根本是原型模式。在 Java 等强类型语言中,原型模式的出现是为了实现类型之间的解耦。而 JavaScript 本身类型就比较模糊,不存在类型耦合的问题,所以平时不会刻意地去使用原型模式。因此在 JS 中把原型模式作为一种编程范式来讨论会更合适。
二、谈原型模式,其实是谈原型范式
原型编程范式的核心思想是利用实例来描述对象,用实例作为定义对象和继承的基础。在 JavaScript 中,原型编程范式的体现就是基于原型链的继承。
三、模拟 Java 中的克隆接口
模拟 Java 中的克隆接口,就是实现 JavaScript 原型模式,即实现深拷贝。