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

概述

首先说明,学习原型链的知识是为了更好的理解原型和原型对象,在实际应用中很少单独使用下面介绍的原型链,具体的原因是下面提到的它的缺点。

我们复习一下之前的知识:

  • 构造函数的prototype指向了原型对象
  • 原型对象中constructor指向了构造函数
  • 实例中的__proto__指向了原型对象

这时候我们如果将一个实例的原型指针,指向另一个对象的实例,这时候这些实例就会串成一个链条,即A实例的__proto__指向B实例,B实例的__proto__又指向其原型对象。下面的代码是原型链的基本实现:

  function SuperType() {
    this.property = true;
  }
  SuperType.prototype.getSuperValue = function() {
    return this.property;
  };
  function SubType() {
    this.subProperty = false;
  }
  SubType.prototype = new SuperType(); //将SubType的原型对象指向SuperType的实例
  SubType.prototype.getSubValue = function() {
    return this.subProperty;
  };
  var instance = new SubType();
  alert(instance.getSuperValue()); // true

其继承关系如下图
最详细的JavaScript高级教程(十七)原型链_第1张图片
注意,SubType的prototype指向SuperType的一个实例,所以SubType的prototype的prototype就指向了SuperType的prototype。(理解清楚,有点绕口)

为了强化这个概念,我们看一个问题,在上面的代码中,instance的constructor属性指向谁呢?答案是指向SuperType的构造函数。为什么呢?我们来分析一下:

  1. 在原型链中SubType的prototype指向SuperType的一个实例
  2. 实例并没有constructor属性,这个属性是在prototype中的,原型对象中的这个属性指向了构造函数
  3. instance没有constructor,要从原型对象中获得constructor
  4. 其原型对象是SuperType的一个实例,这个实例也没有constructor,要从自己的prototype中获得
  5. 从SuperType的一个实例的prototype中获得的constructor就是SuperType的构造函数

默认原型Object

我们之前说过,如果写了一个构造函数,就会创建一个默认的原型对象,现在我们学习了原型链之后我们就能进一步完整这个理解。

创建的默认的原型对象的prototype指向Object,从Object中继承了toString ValueOf等方法。所以完整的原型链如下如:
最详细的JavaScript高级教程(十七)原型链_第2张图片
注意SuperType的prototype指向Object,而SubType的prototype原本也指向Object,我们将其指向了Supertype的实例,实现了原型链。

注意在上面的图中,SubType Prototype其实就是SuperType的一个实例,所以它的Prototype才指向了SuperType的Prototype,同理,Supertype Prototype也是Object的一个实例。

对象识别

  • instanceof 这个操作符可以判断实例的任意一个父构造函数
    alert(instance instanceof Object); //true
    alert(instance instanceof SuperType); //true
    alert(instance instanceof SubType); //true
    
  • isPrototypeOf 判断实例的原型,任意在链上的原型都可以
    alert(Object.prototype.isPrototypeOf(instance)); //true
    alert(SubType.prototype.isPrototypeOf(instance)); //true
    alert(SuperType.prototype.isPrototypeOf(instance)); //true
    

复写方法

注意复写方法一定要在替换原型对象之后,这个也很好理解,因为我们在替换了原型对象之后,指针就改变了。

原型链的问题

  1. 实例不复写直接使用原型中的引用类型属性,这个属性会在所有的实例中共享。这是我们不希望看到的。
    function SuperType() {
        this.property = ['red'];
    }
    function SubType() {}
    SubType.prototype = new SuperType(); //将SubType的原型对象指向SuperType的实例
    var instance = new SubType();
    instance.property.push('blue');
    var ins2 = new SubType();
    alert(ins2.property); //red,blue 无法各自拥有自己的,原先属于原型对象的引用类型
    
  2. 创建子类型的时候不能向超类型的构造函数中传递参数

你可能感兴趣的:(JavaScript高级编程)