最详细的JavaScript高级教程(十八)高级原型链

本文详细探讨了JavaScript中的继承方式,包括经典继承、组合继承、原型式继承和寄生组合继承。重点介绍了如何利用构造函数和原型来实现继承,以及它们各自的优缺点。寄生组合继承被推荐为标准继承方式,因为它更有效地处理了性能和原型链的问题。
摘要由CSDN通过智能技术生成

经典继承(借用构造函数)

为了解决之前提到的超类型构造函数中引用类型的问题,我们可以使用借用构造函数的方式

  function SuperType() {
    this.property = ['red'];
  }
  function SubType() {
    SuperType.call(this); // 借用构造函数
  }
  var instance = new SubType();
  alert(instance instanceof SubType);
  alert(instance instanceof SuperType);

我们分析一下这种方式:

在SubType中通过call,让SuperType的构造函数在自己的运行环境中运行,SuperType中的this就指向了SubType的作用域,等于给SubType添加了property的对象,故实现了每个实例有自己独立的超类型引用对象。

但是这种方法缺点太过于明显:

  1. 这样写,这两个对象没有继承关系了,像代码中使用的instanceof运算符,不能确定实例和超类的关系了。
  2. 之前我们在创建对象的时候讲过,将对象属性的定义写道构造函数中,将方法的定义写到原型中可以避免方法的重复创建,这种模式无法识别不写在构造函数中的代码。

组合继承(推荐)

其实上面的方式我们已经很接近成功了,为了解决缺点1,我们把SubType的原型指向SuperType的一个实例看看会发生什么情况。

  function SuperType() {
    this.property = ['red'];
  }
  function SubType() {
    SuperType.call(this);
  }
  SubType.prototype = new SuperType();
  var instance = new SubType();
  alert(instance instanceof SubType);
  alert(instance instanceof SuperType); //true
  var instance2 = new SubType();
  instance.property.push('blue');
  alert(instance2.property); //red

我们发现,当我们这么做的时候,既维持了继承关系,也为每一个实例创建了自己的property属性。

这种方式是我们在实际应用中最常使用的方式。

原型式继承

当我们不需要创建一类型实例,而是只是想简单的创建一个对象的副本的时候,我们既可以用原型式继承,其作用就像是创建了对象的一个克隆,但是这个克隆是浅复制的。

其原理很简单,就是下面的方法

// 本质是返回一个以传入对象为原型对象的实例
function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

这种方法可以轻量级的创建一个之前对象的副本,注意其是浅复制的,无法避免引用类型的问题。

ES5对于这种创建方式进行了规范化,使用下面的方法 Object.create:

  var person = {
    name: 'nic',
    friend: ['red']
  };
  var another = Object.create(person);
  var another2 = Object.create(person, {
    name: {
      // 注意这里的属性描述必须是一个对象
      value: 'Greg'
    }
  });
  alert(another2.name);

注意第二个参数是需要复写的属性的属性描述符,不能直接写 属性:值。

寄生组合继承(标准继承)

当我们使用组合继承的时候,需要把子类型的prototype属性赋值为父类型的一个实例,这就导致父类型的构造函数被调用了两次,一次是在SuperType.call(this);的时候,一次是在SubType.prototype = new SuperType();的时候。构造函数多次的执行会导致性能的消耗,所以为了避免这种情况,结合我们之前讲的对象复制的方法,我们使用下面这种寄生组合模式。

  function inheritPrototype(subType, superType) {
    // 使用复制的方法,创建出的对象不会打破原型链
    // 如果不理解这里,返回去看object.create的实现
    var prototype = Object.create(superType.prototype); 
    prototype.constructor = subType;
    subType.prototype = prototype;
  }
  function SuperType() {
    this.property = ['red'];
  }
  function SubType() {
    SuperType.call(this);  //引用类型都复制出了副本
  }
  inheritPrototype(SubType, SuperType);
  var instance = new SubType();
  alert(instance instanceof Object); //true
  alert(instance instanceof SuperType); //true
  alert(instance instanceof SubType); //true

我们注意下面几点:

  1. 为什么说用Object.create节省了性能呢?因为Object.create的实现是:
    function object(o) {
        // 这里等于返回了一个o的构造函数中没有代码的子类
        // 在寄生组合模式中,返回的这个子类正好用于做SubType的prototype
        function F() {} 
        F.prototype = o;
        return new F();
    }
    
    其中,F构造函数中没有代码,节省性能,而调用SuperType的构造方法,里面代码很多,初始化了很多属性,但是在SuperType.call(this);的时候,这些属性又被重新初始化出了一个副本,所以这部分性能就等于浪费掉了
  2. 由于前面讲的Object.create的实现,所以不影响原型链
  3. 注意inheritPrototype中constructor的引用指向要处理对
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值