1. 属性拷贝继承
//创建父级对象
var parent = {
name: 'xiaoming',
age: '40',
sex: 'male',
showName: function(){
console.log(this.name);
}
}
//创建子级对象
var child = {};
//通过遍历将父级对象的属性和方法赋值给子级对象
for(var i in parent){
child[i] = parent[i];
}
console.log(child);
存在问题:
如果继承的是引用类型的属性时,父级对象与子级对象的属性是共享的,无论谁修改属性,父子对象都会受到影响
2. 原型式继承
原型式继承是借用构造函数的原型对象实现继承,即
子构造函数 . prototype = 父构造函数 . prototype
// 创建父级函数
function Parent (name){
this.name = name;
this.showName = function(){
console.log(this.name);
}
}
// 设置父级函数的原型对象
Parent.prototype.age = 18;
Parent.prototype.friends = [ '小明', '小红' ];
Parent.prototype.showAge = function(){
console.log(this.age);
}
// 创建子级函数
function Child (){
}
// 通过原型继承父级函数的属性
Child.prototype = Parent.prototype;
var child = new Child();
child.age // 18
child.friends // [ '小明', '小红' ]
child.showName // undefined
这样一来 child 就继承了 Parent 原型对象上的属性,但仍存在以下问题:
- 父级构造函数的原型对象和子级构造函数的原型对象存在共享问题,不能随便改动自己的原型,否则双方都会受到影响
- 只能继承父构造函数的原型对象上的成员, 不能继承父构造函数的实例对象的属性和方法
3. 原型链式继承
子类可以继承父类构造函数原型对象和实例上的属性和方法
子构造函数.prototype = new 父构造函数()
// 创建父级构造函数
function Parent(){
this.name = '小明';
this.showName = function (){
console.log(this.name);
}
}
// 设置父级函数的原型对象
Parent.prototype.age = 18;
Parent.prototype.showAge = function(){
console.log(this.age);
}
// 创建子级构造函数
function Child (){
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
var child = new Child();
child.name // '小明'
child.age // 18
child.showAge() // 18
存在问题:
- 继承了太多没用的属性
- 不能给父级构造函数传递参数
- 父子构造函数的原型对象仍然存在共享问题
4. 借用构造函数实现继承
使用 call 和 apply 借用构造函数,可以解决给父类构造函数传参的问题
// 创建父级构造函数
function Parent (name, sex, age){
this.name = name;
this.sex = sex;
this.age = age;
}
Parent.prototype.showAge = function(){
console.log(this.age);
}
// 创建子级构造函数
function Child (name, sex, age){
//使用 call 借用 Parent 构造函数
Parent.call(this, name, sex, age);
}
var child = new Child('小明', 'male', 18);
child.name // '小明'
child.age // 18
child.showAge() //报错
存在问题:
- 不能调用构造函数原型对象的属性和方法
- 增加了函数的调用
5. 组合继承(借用构造函数+原型式继承)
解决了第4种方法中子类同时继承父类构造函数原型对象和实例上的属性和方法问题
// 创建父级构造函数
function Parent (name, sex, age){
this.name = name;
this.sex = sex;
this.age = age;
}
Parent.prototype.showAge = function(){
console.log(this.age);
}
// 创建子级构造函数
function Child (name, sex, age){
//使用 call 借用 Parent 构造函数
Parent.call(this, name, sex, age);
}
// 通过原型继承父级函数的属性
Child.prototype = Parent.prototype;
Child.prototype.constructor = Child;
var child = new Child('小明', 'male', 18);
child.name // '小明'
child.age // 18
child.showAge() // 18
存在问题:
- 父子构造函数的原型对象仍然存在共享问题
6. 圣杯模式
解决了父子构造函数之间的共享问题,同时子级能够继承父级原型和实例的所有属性,并且可以传参
// 创建父级构造函数
function Parent (name, sex, age){
this.name = name;
this.sex = sex;
this.age = age;
}
Parent.prototype.showAge = function(){
console.log(this.age);
}
// 创建一个空函数
function F(){
}
// 创建子级构造函数
function Child(){
}
// 用空函数 F 的原型对象继承父级构造函数的原型对象,再让 Child 的原型对象继承 F 的实例
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
// 手动设置 uber 属性,表明 Child 最终继承自 Parent
Child.prototype.uber = Parent.prototype;
var child = new Child('小明', 'male', 18);
child.name // '小明'
child.age // 18
child.showAge() // 18
以上代码可以封装为一个继承函数方法
function extend(Target, Origin){
function F(){};
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target;
Target.prototype.uber = Origin.prototype;
}
以上便是JS中实现继承的几种方法,运用的时候需结合实际情况,选择最适合的方法即可