原型模式

2022-03-01  本文已影响0人  小丸子啦啦啦呀

此篇文章仅作为一篇读后感,方便个人记忆,如果需阅读更加详细以及权威的内容,移步史上最全设计模式导学目录

在阅读刘老师写的原型模式的时候,我脑海中不停在想:Javascript中的prototype和设计模式领域中的原型模式有什么区别和联系呢?

Javascript中的原型

为了弄清楚这个问题,先得搞清楚,JS中的原型是什么?有什么作用?

我们创建的每个函数都有一个 prototype(原型)属性,这个属性是一个指针,指向一个对象,
而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
--引用自 红宝书6.2.3节 原型模式


红宝书中提到,在创建对象时,如果用简单工厂,那么多个实例之间本来有一些可以共享的方法或者属性,便不得不重复被创建,而原型模式解决了这一问题。

image.png

拿上图举例,有了Person prototype,可以实现person1和person2上的sayName是同一个函数。

除了以上所说的使用原型模式来创建对象之外,使用原型链来实现继承也是一大用处。

简单回顾一下构造函数、原型和实例的关系:每
个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型
对象的内部指针。那么,假如我们让原型对象等于另一个类型的实例,结果会怎么样呢?显然,此时的
原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数
的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实
例与原型的链条。这就是所谓原型链的基本概念
-----引用自红宝书 6.3.1 原型链

image.png

如上图,subType的原型是SuperType的实例,这样subType就拥有了SuperTypede方法。

分析到这里,原型其实就是一个对象,每个构造函数都有一个prototype指向它的原型,通过它可以实现继承,实例间共享属性和方法。

设计模式领域中的原型模式

那么,设计模式领域的原型模式是什么呢?
直接来看一个设计模式领域的原型模式案例:

function Person(name){
  this.name = name;
}
Person.prototype.sayHi(){
  console.log("hi I'm "+this.name)
}
Person.prototype.clone(){
  return new Person(this.name)
}

const coco = new Person("coco");
const coco2 = coco.clone();
coco2.sayHi(); // hi I'm coco

不难看出,设计模式领域的原型模式是基于一个已有的实例,再复制出一个实例。

const coco = new Person("coco"); // prototype pattern in JS
const coco2 = coco.clone();// prototype pattern in design pattern

原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。

深拷贝与浅拷贝

刘老师博客中提到了,clone分deepClone 和 shallowClone, 那么我们改造一下上面的例子,试试看上述方法实现的是深拷贝还是浅拷贝;

function Person(name){
  this.name = name;
  this.friends = ["Ross", "Chandler"]
}
Person.prototype.sayHi(){
  console.log("hi I'm "+this.name)
}
Person.prototype.clone(){
  return new Person(this.name)
}

const coco = new Person("coco");
const coco2 = coco.clone();
coco2.friends.push("Rachel");
console.log(coco.friends); // ["Ross", "Chandler"]
console.log(coco2.friends); // ["Ross", "Chandler", "Rachel"]

可以看到,clone出来的副本拥有了自己的一份朋友,和之前的实例已经完全脱开了联系,从这层意义上而言,是深拷贝了。

关于Object.create

我们知道,在寄生组合式继承方法中,Object.create方法可以基于父类的原型创建一个新的对象出来:

function superType(value){
  this.value = value;
  this.queue = [1,2,3]
}
superType.prototype.show = function(){
  console.log(this.value)
}

function subType(value){
  superType.call(this, value);
}

// copied a superType.prototype
// change the constructor reference forcely
subType.prototype = Object.create(superType.prototype, {
  constructor: {
     value: subType
  }
})

const sub = new subType("coco");
sub.show();
coco.queue.push(4);

console.log(new subType("momo").queue); // still [1,2,3]

那么Object.create能否用来拷贝对象呢?让我们来试一试:

function Person(name){
  this.name = name;
  this.friends = ["Ross", "Chandler"]
}
Person.prototype.sayHi=function(){
  console.log("hi I'm "+this.name)
}

const coco = new Person("coco");
const coco2 = Object.create(coco, {
  age: 18; // add extra property
});
console.log(coco2.__proto__ ===  coco); // true
coco2.friends.push("Rachel") // 因为coco2自己身上没有friends, 所以改的其实就是它的原型coco上的friends
console.log(coco.friends); // [["Ross", "Chandler", "Rachel"]
console.log(coco2.friends); // ["Ross", "Chandler", "Rachel"]

可以看到,在副本上的修改会直接影响到原版对象,这并不是我们想要的效果,所以用Object.create来实现拷贝一个对象是不太合适的。

总结

js中的原型模式和设计模式领域中的原型模式,相同点在于都可以利用prototype作为一个蓝本来创建对象,但是js中的原型更多偏向于创建空副本,而设计模式领域的原型模式偏向于创建带属性的副本。

上一篇 下一篇

猜你喜欢

热点阅读