这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战
原型式继承
前言
.前面我们已经介绍了JavaScript的三种继承方式:原型链,借用构造函数以及二者的组合继承。其中第三种组合继承最为常用。因为我们知道它结融合了原型链和借用构造函数的优点,隐藏了自己各自的缺点,最终实现了相对比较完美的继承方式。接下来我们要介绍js中的第四种继承方式——原型式继承。
原型式继承
曾经有人提出了另一种实现继承的方法,这种方法并没有使用严格意义上的构造函数,而是借助原型可以基于已有的对象来创建一个新对象, 同时还不必因此创建自定义类型。下面我们来看一段代码
function object(o){
function F(){}
F.prototype = o;
return new F();
}
以上代码在object函数内部,先创建了一个临时的构造函数,然后将传入的对象作为该构造函数的原型,最后返回了这个临时类型的新实例。从本质上讲,object() 对传入其中的对象执行了一次浅复制。来看下面的例子
var person = {
name: 'Alvin',
friends: ['Yannis','Ylu']
}
var p1 = object(person);
p1.name = 'Bob';
p1.friends.push('Lucy');
var p2 = object(person);
p2.name = 'Lilei';
p2.friends.push('Hanmeimei');
console.log(person.friends);//Yannis, Ylu, Lucy, Hanmeimei
这种原型式继承要求必须有一个对象可以作为另一个对象的基础。如果有这么一个对象的话, 可以把它传递给object函数,然后再根据具体需求对得到的对象加以修改。在这个例子中可以作为另一个对象基础的是person对象,于是我们把它传入到object函数中,然后该函数就会返回一个新对象,这个新对象将person作为原型,所以它的原型中就包含了一个基本类型属性name和一个引用类型属性friends。这就意味着person.friends不仅属于person所有,而且也会被p1和p2共享。实际上就相当于创建了person对象的两个副本。
在ECMAScript5中新增了Object.create()方法,该方法规范了原型式继承。这个方法接收两个参数:一个用作新对象原型的对象,另一个是可选的,用于新对象定义额外的属性的对象。在只传入一个参数的情况下,Object.create()与上面的object()方法行为相同。看下面示例:
var person = {
name: 'Alvin',
friends: ['Yannis','Ylu']
}
var p1 = Object.create(person);
p1.name = 'Bob';
p1.friends.push('Lucy');
var p2 = Object.create(person);
p2.name = 'Lilei';
p2.friends.push('Hanmeimei');
console.log(person.friends);//Yannis, Ylu, Lucy, Hanmeimei
Object.create()方法的第二个参数与Object.defineProperties()方法的第二个参数格式相同:每个属性都是通过自己的描述符定义。以 这种方式指定任何属性都会覆盖原型对象上的同名属性。如:
var person = {
name: 'Alvin',
friends: ['Yannis','Ylu']
}
var p1 = Object.create(person,{
name:{
value:'Lucy'
}
})
console.log(p1.name);//Lucy
使用场景
在没有必要兴师动众的创建构造函数,而只想让一个对象与另一个对象保持类似的情况下,原型式继承是完全可以考虑的。当然同样的问题就是:包含的所有引用类型的属性始终都会共享相应的值。
寄生式继承
寄生式继承是与原型式继承紧密相关的一种思路。寄生式继承的思路与寄生构造函数和工程模式类似,即创建一个仅用于封装继承过程的函数,该函数的内部以某种方式来增强对象,最后再像真的是它做了所有工作一样返回对象。下面的代码示范了寄生式继承的模式
function object(o){
function F(){}
F.prototype = o;
return new F();
}
function createAnother(original){
var clone = object(original);//通过调用函数创建一个新对象
clone.sayHi = function(){//以某种方式来增强这个对象
console.log('hello')
}
return clone;//返回这个对象
}
在这个例子中createAnother函数接收了一个参数,也就是将要作为新对象基础的对象。然后,把这个对象传递给object(),将返回的结果赋值给clone,再为clone对象添加一个新的方法sayHi(),最后返回clone对象。可以像下面这样使用createAnother函数:
var person = {
name:"Alvin",
friends:['Yannis',"Lucy"]
}
var anotherPerson = createAnother(person);
anotherPerson.sayHi();//hello
这个例子中的代码基于person返回了一个新对象-anotherPerson。新对象不仅具有person的所有属性和方法,而且还有自己的sayHi()方法。
在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。前面示范继承模式时使用的object()函数不是必须。任何能够返回新对象的函数都可以。
使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率;这点与构造函数模式类似。