首先回顾一下原型式继承:
function object(obj){
function F(){}
f.prototype = obj;
return new F();
}
寄生式继承是与原型式继承紧密相关的一种思路,并且同样也是由克罗克福德推而广之的。
说到寄生式继承不得不说工厂模式和寄生构造函数模式创建对象。下面来回顾一下工厂模式创建对象:
function createPerson(name, age, job){
let o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
console.log(this.name);
}
return o;
}
let p = createPerson('bob', 18, 'JavaScript');
p.sayName();//bob
在函数体内创建了一个对象,然后对这个对象进行增强,添加一些属性和方法,再返回这个对象,使用时直接调用createPerson
,并将参数传给它,就得到了我们想要的对象,但本质上 createPerson
不过是封装了用于增强o
的代码。
再看寄生构造函数模式创建对象:
function Person(name, age, job){
let o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
console.log(this.name);
}
return o;
}
let p = new Person('bob', 18, 'JavaScript');
p.sayName();//bob
思路其实和工厂模式一样,只不过创建对象是通过new关键字进行的,而工厂模式是直接调用。
寄生式继承的思路就是类似上面这样:创建一个用于封装继承过程的函数,该函数内部以某种方式来增强对象,然后返回这个对象,该对象就是原对象的一个子对象。来看下面的示例:
function extend(obj){
let child = object(obj);//或者使用Object.create(obj);
child.sayHi = function(){
console.log('hi');
}
return child;
}
创建extend
函数,接收的参数就是将要继承的对象。函数体内使用object
函数返回了obj
的子对象child
,然后通过给child
添加sayHi
方法,对child
进行了增强,最后返回child
。与原型式继承相比,寄生式继承帮我们封装了增强的代码,而原型式继承需要我们手动书写增强的代码。来看下面的示例:
let p = {
name: 'bob',
friends: ['jack', 'rose']
}
//使用寄生式继承
let p2 = extend(p);
p2.sayHi();//hi
//使用原型式继承
let p3 = object(p);
p3.sayHi = function(){
console.log('hi');
}
p3.sayHi();//hi
在JavaScript继承(四)——原型式继承中提到的Object.create
方法,其可以接受一个或两个参数,接收一个参数时等同于object函数,也就是原型式继承,其实接收两个参数就等同于寄生式继承。来看下面的示例:
let p4 = Object.create(p, {
sayHi: {
value: function(){
console.log('hi');
}
}
});
p4.sayHi();//hi
所以无论是原型式继承还是寄生式继承,使用Object.create
就够了。
实际上,寄生式继承基本上可以替代原型式继承,寄生式继承可以看作原型式继承的增强。需要注意的是使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率,这一点与构造函数模式类似。关于构造函数模式,如下所示:
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
}
}
let p1 = new Person('zhangsan', 18, 'JavaScript');
let p2 = new Person('lisi', 20, 'Java');
可以参考这篇文章--JavaScript创建对象(二)——构造函数模式。