javaScript中的继承

js中的继承主要是根据原型链来实现的

原型链继承

原型链继承的主要思想就是根据原型来继承多个引用类型的属性或方法

function Super() {
    this.name = 'a'
}
function Sub() {}
// 继承Super
Sub.prototype = new Super();
let sub1 = new Sub()
console.log(sub1.name)

将子类的prototype指向父类的实例,这样的话可以通过原型来来访问父类的属性和方法,但是这样需要重置Sub.prototype.constructor,因为此时子类的peototype.constructor指向的是父类的构造函数

父类的实例属性如果是引用类型的,那么子类其中一个实例更改数据,会影响到另一个实例的数据

function Super() {
    this.color = ['red', 'green']
  }
  function Sub() {}
  Sub.prototype = new Super();
  let sub1 = new Sub();
  let sub2 = new Sub();
  sub1.color.push('yellow');
  console.log(sub1.color); // ["red", "green", "yellow"]
  console.log(sub2.color); // ["red", "green", "yellow"]

原型链继承的缺点

  • 对于引用类型的数据,子类实例一改全改。
  • 子类引用父类的原型方法时,无法传参。
借用构造函数继承

对于原型链继承的两个缺点,开发人员开始使用一种叫借用构造函数的继承方法。函数只是在特定环境中执行代码的对象,因此可以通过apply/call来改变父类的this指向。

  function Super() {
    this.color = ['red', 'green']
  }
  function Sub() {
    Super.call(this)
  }
  let sub1 = new Sub();
  let sub2 = new Sub();
  sub1.color.push('yellow');
  console.log(sub1.color);  // ["red", "green", "yellow"]
  console.log(sub2.color); //  ["red", "green"]

通过更改this指向,使得每一个实例都有一份color属性的副本,就不会一改全改,而且借用构造函数继承也可以进行传参

  function Super(name) {
    this.name = name
  }
  function Sub() {
    Super.call(this,'coder')
  }
  let sub1 = new Sub();
  console.log(sub1.name); // 'coder'

借用构造函数继承的缺点

  1. 必须在构造函数中定义方法,函数无法重用
  2. 子类无法访问到父类原型上的方法

基于这两个缺点,借用构造函数继承往往无法单独使用

组合继承

组合继承结合了原型链继承和借用构造函数继承,即使用原型链继承原型上的属性和方法,使用借用构造函数来继承实例上的属性

    function Super(name) {
        this.name = name;
        this.color = ['red', 'green']
    }
    Super.prototype.say = function() {
        console.log(this.name);
    }
    function Sub() {
        Super.call(this, 'tom'); // 借用构造函数继承实例属性
        this.age = 12;
    }
    Sub.prototype = new Super(); // 原型链继承原型上的方法
    let sub1 = new Sub();
    sub1.color.push('yellow')
    let sub2 = new Sub();
    console.log(sub1.color); // ["red", "green", "yellow"]
    console.log(sub2.color); // ["red", "green"]
    sub2.say(); // tom

组合继承弥补了原型链继承和借用构造函数继承的缺点,同时也保留了isPrototypeOfinstanceof识别合成对象的能力,成为了当前js使用范围最广的一种继承方式

// prototypeObj.isPrototypeOf(object)
// isPrototypeOf检测prototypeObj是否出现在object的原型链上
console.log(Super.prototype.isPrototypeOf(sub2)); // true
console.log(sub2 instanceof Super); // true

组合继承会有效率问题,我们在父类函数执行console.log(1),会发现执行了三次

 function Super(name) {
     console.log(1)
     this.name = name;
     this.color = ['red', 'green']
 }

第一次是在Sub.prototype = new Super()时执行
第二次和第三次分别是在创建子类实例的时候执行

寄生式继承

寄生式继承是比较接近原型链继承的一种方法
创建一个实现继承的函数,以某种方法来增强对象,然后返回这个对象

let person = {
   name:'Tom'
}
function createAnother(object) {
    let obj = Object.create(object);
    console.log(obj)
    obj.say = function() {
        console.log(this.name)
    }
    return obj
}
let sub = createAnother(person);
sub.say()

Object.create() 创建一个新对象,将新对象的原型指向现有的对象,然后返回这个新对象,会接收两个参数,第一个是要指向的对象,是必传参数,第二个参数类似于Object.defineProperty的第三个参数,是可选参数

寄生式组合继承

基本思路:不通过调用父类来给子类原型赋值,而是通过取得父类的一个副本

function Super(name) {
     console.log(1)
     this.name = name;
     this.color = ['red', 'green']
 }
 Super.prototype.say = function() {
     console.log(this.name);
 }
 function Sub() {
     Super.call(this, 'Tom');
     this.age = 12;
 }
 function inheritPrototype(subType, superType) {
     let prototype = Object.create(superType.prototype); // 新对象指向父类的原型
     subType.prototype = prototype; // 将新对象赋值给子类原型
     subType.prototype.constructor = subType; // 由于改写了子类原型,所以需要重新复制constructor
 }
 inheritPrototype(Sub, Super)
 let sub1 = new Sub();
 sub1.color.push('yellow')
 let sub2 = new Sub();
 console.log(sub1.color);  // ["red", "green", "yellow"]
 console.log(sub2.color);  // ["red", "green"]
 sub2.say() // Tom

寄生式组合继承算是引用类型继承的最佳实践

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值