JS继承常见的几种方式

原型链继承

原型继承很简单,就是将子类的原型挂载到父类实例上。

// 一个父类
function Father(){
  this.name = "xhc",
  this.age = 21,
  this.hobbies = [] // 引用类型属性
}

// 父类原型上的属性/方法
Father.prototype.getAge = function(){
  console.log(this.age)
}

// 一个子类
function Child(){}
Child.prototype = new Father(); // 原型链继承的方式

let child1 = new Child();
console.log(child1.name);  // xhc -- 继承了父类的属性
child1.getAge(); // 21 -- 调用了父类原型的方法

// 测试引用类型共享问题
child1.hobbies.push("ping-pong")
console.log(child1.hobbies); // ["ping-pong"]
let child2 = new Child();
console.log(child2.hobbies); // ["ping-pong"]

这种方法实现继承,有个明显的优点就是简单。另外还有下面这些特点:

  • 只是单一继承无法实现多继承。

这一点很好理解,Child.prototype只能等于一个值,无法同时又等于其他值。

  • 新实例无法向父类构造函数传参。

  • 来自父类以及父类所有属性会被所有实例共享。

原型上的属性是共享的,一个实例修改了原型属性,另一个实例的原型属性也会被修改!

构造器继承

构造器继承借用了call等可以改变this指向的函数来实现继承。有个很明显的优点就是可以在父类构造器中传入参数。(这个可以比较一下原型链继承)

// 一个父类
function Father(faName,faAge){
  this.name = faName,
  this.age = faAge
}

// 父类原型上的属性/方法
Father.prototype.getAge = function(){
  console.log(this.age)
}

// 一个子类
function Child(childName, childAge){
  Father.call(this, childName, childAge); // 构造器继承
}

let child = new Child('xhc', 21);
console.log(child.name);  // xhc -- 构造器可以传参
child.getAge(); // TYPEERROR -- 无法继承父类原型上的属性
特点缺陷
1.只继承了父类的属性和方法,没有继承父类原型的属性和方法1.只能继承父类构造函数的属性
2.可以实现多继承,通过call方法可以实现2.无法实现构造函数的调用,每次用的时候都要调用一次父类构造函数
3.可以向父类构造函数传参3.无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
4.解决了子类实例共享父类引用属性的问题4.实例只是子类的实例,不是父类的实例。(用instanceof可以检测出来)

组合继承

这种继承方式就是结合了原型链继承和构造函数继承,合二为一,提取各自的特点来实现继承。

// 一个父类
function Father(faName,faAge){
  this.name = faName,
  this.age = faAge,
  this.hobbies = [] // 实例引用属性
}

// 父类原型上的属性
Father.prototype.getAge = function(){
  console.log(this.age)
}

// 一个子类
function Child(childName, childAge){
  Father.call(this, childName, childAge); // 组合继承核心1
}
Child.prototype = new Father(); // 组合继承核心2

let child1 = new Child('xhc', 21);
console.log(child1.name);  // xhc -- 构造器可以传参
child1.getAge(); // xhc -- 继承了父类原型方法

// 测试一下实例引用问题
child1.hobbies.push('ping-pong');
console.log(child1.hobbies); // ['ping-pong']

let child2 = new Child('lyc', 21);
console.log(child2.hobbies); // [] -- 这里说明了不存在引用类型属性共享问题

这样的话,组合继承就是前面两种继承的结合体了:

特点缺陷
1.可以向父类构造函数传递参数1.调用了两次父类构造函数(耗内存)
2.继承了父类以及父类原型上的属性和方法2.子类的构造函数会代替原型上的那个父类构造函数
3.不存在引用属性共享问题
4.每个新实例引入的构造函数属性是私有的
5.既是父类的实例,也是子类的实例

寄生组合继承

寄生组合继承是对组合继承的优化,组合继承的最大缺点就是调用了两次构造函数,针对这个,出现了寄生组合继承。

// 一个父类
function Father(faName,faAge){
  this.name = faName,
  this.age = faAge,
  this.hobbies = [] // 实例引用属性
}

// 父类原型上的属性
Father.prototype.getAge = function(){
  console.log(this.age)
}

// 一个子类
function Child(childName, childAge){
  Father.call(this, childName, childAge); //
}
// Child.prototype = new Father(); // 下面两行取代了这一行
Child.prototype = Object.create(Father.prototype)
Child.prototype.constructor = Child; // 重新指定constructor

let child1 = new Child('xhc', 21);
console.log(child1.name);  // xhc -- 构造器可以传参
child1.getAge(); // xhc -- 继承了父类原型方法

// 测试一下实例引用问题
child1.hobbies.push('ping-pong');
console.log(child1.hobbies); // ['ping-pong']

let child2 = new Child('lyc', 21);
console.log(child2.hobbies); // [] -- 这里说明了不存在引用类型属性共享问题
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值