1. 原型链继承
通过原型链机制实现继承。如下代码所示:
<script>
// 宠物构造函数
function Pet(name){
this.name = name || '无';
}
Pet.prototype.getName = function(){
return this.name;
};
Pet.prototype.setName = function(name){
this.name = name;
};
Pet.prototype.eat = function(){
console.log('吃饭');
};
// 狗狗构造函数
function Dog(name, strain){
this.name = name;
this.strain = strain || '中华田园犬';
}
// 让创建对象继承自宠物对象
Dog.prototype = new Pet();
Dog.prototype.constructor = Dog;
Dog.prototype.getStrain = function(){
return this.strain;
};
Dog.prototype.setStrain = function(strain){
this.strain = strain;
};
// 覆盖原型方法
Dog.prototype.eat = function(){
console.log('吃狗粮');
};
// 测试
var dog = new Dog('欢欢', '黑贝');
console.log('名字:' + dog.getName());
console.log('品种:' + dog.getStrain());
dog.eat();
</script>
说明:以上示例中,似乎 Pet
构造函数的参数并没有什么用处,我们暂且不管,后面再解释。
2. 原型链的问题
继承中引用数据类型的问题 如果父类中除了基本数据类型外,还有引用数据类型,那么,原型继承可能出现问题。
<script>
// 宠物构造函数
function Pet(name){
this.name = name || '无';
this.hacks = ['叼飞盘', '拉雪橇'];
}
Pet.prototype.getName = function(){
return this.name;
};
Pet.prototype.setName = function(name){
this.name = name;
};
Pet.prototype.eat = function(){
console.log('吃饭');
};
// 狗狗构造函数
function Dog(name, strain){
this.name = name;
this.strain = strain || '中华田园犬';
}
// 让创建对象继承自宠物对象
Dog.prototype = new Pet();
Dog.prototype.constructor = Dog;
Dog.prototype.getStrain = function(){
return this.strain;
};
Dog.prototype.setStrain = function(strain){
this.strain = strain;
};
// 覆盖原型方法
Dog.prototype.eat = function(){
console.log('吃狗粮');
};
// 测试
var xx = new Dog('笑笑', '牧羊犬');
xx.hacks.push('看羊群');
console.log('名字:' + xx.getName());
console.log('品种:' + xx.getStrain());
console.log(xx.hacks);
console.log();
var hh = new Dog('欢欢', '黑贝');
console.log('名字:' + hh.getName());
console.log('品种:' + hh.getStrain());
console.log(hh.hacks);
</script>
说明:我们看到笑笑向 hacks
添加了 '看羊群' 技能,在欢欢中也看到了,这不是我们的本意,我们本意是想,各只狗狗都有自己的技能。 这是因为 Dog
原型对象的属性 hacks
是引用类型,只创建了一个对象,所有的狗狗对象都指向同一原型中的 hacks
属性。应该为每个狗狗创建自己的 hacks
数组。
3. 组合继承
使用构造函数借用解决引用数据类型的问题。
<script>
// 宠物构造函数
function Pet(name){
this.name = name || '无';
this.hacks = ['叼飞盘', '拉雪橇'];
}
Pet.prototype.getName = function(){
return this.name;
};
Pet.prototype.setName = function(name){
this.name = name;
};
Pet.prototype.eat = function(){
console.log('吃饭');
};
// 狗狗构造函数
function Dog(name, strain){
Pet.call(this, name); // !!!替换 this.name = name;
this.strain = strain || '中华田园犬';
}
// 让创建对象继承自宠物对象
Dog.prototype = new Pet();
Dog.prototype.constructor = Dog;
Dog.prototype.getStrain = function(){
return this.strain;
};
Dog.prototype.setStrain = function(strain){
this.strain = strain;
};
// 覆盖原型方法
Dog.prototype.eat = function(){
console.log('吃狗粮');
};
// 测试
var xx = new Dog('笑笑', '牧羊犬');
xx.hacks.push('看羊群');
console.log('名字:' + xx.getName());
console.log('品种:' + xx.getStrain());
console.log(xx.hacks);
console.log();
var hh = new Dog('欢欢', '黑贝');
console.log('名字:' + hh.getName());
console.log('品种:' + hh.getStrain());
console.log(hh.hacks);
</script>
说明:
-
我们只修改了一句代码,将
this.name = name;
替换为Pet.call(name)
; -
这种方式叫做构造函数借用。我们借用
Pet
构造函数,在构造新Dog
时,在当前对象上单独创建一个hacks
数组; -
同时,也能将
name
赋值在当前对象上,所以,能够完全替换原来的赋值代码。 -
以上这种利用了原型链,同时使用了构造函数借用的继承方式,我们称为组合继承。
4. ES6 中新的原型继承的方式
<script>
// 3. ES6 新的 原型继承的方式,无需借助函数
var parent = {name : '无名'};
var son = new Object(parent);
son.age = 18;
console.log('son.name = ' + son.name);
console.log('son.age = ' + son.age);
</script>