ECMAScript原型链

构造函数、原型和实例之间的关系

关系图:

原型链的基本概念

基于上述关系图的理解,让原型对象等于另一个类型的实例,假如这个类型的原型又等于另一个类型的实例,这样层层递进,构成了实例和原型的链条。

关系图:

原型链的代码实现的基本模式

//组合构造函数模式和原型模式
function SuperType() {
    this.property = true;
}
SuperType.prototype.getSuperValue = function() {
    return this.property;
};

function SubType() {
    this.subproperty = false;
}

//继承了SuperType
SubType.prototype = new SuperType();

SubType.prototype.getSubValue = function() {
    return this.subproperty;
};

var instance = new SubType();
console.log(instance.getSuperValue());  //true
console.log(instance.constructor === SuperType);  //true
console.log(SubType.prototype.constructor === SuperType);  //true

SubType继承了SuperType。继承是通过创建SuperType实例,然后赋值给SubType.prototype实现的。实现的本质是重写原型对象,代之以一个新类型的实例。

代码关系图:

关系结果:instance指向SubType的原型,Sub-Type的原型又指向SuperType的原型

注意两点:

  1. getSuperValue()方法仍然还在SuperType.prototype中,但property则位于Sub-Type.prototype中。这是因为property是一个实例属性,而get-SuperValue()则是一个原型方法。既然SubType.prototype现在是SuperType的实例,那么property当然就位于该实例中了。
  2. instance.constructor现在指向的是SuperType。原因:SubType 的原型指向了另一个对象——SuperType 的原型,而这个原型对象的constructor 属性指向的是SuperType。

通过实现原型链,本质上扩展了原型搜索机制。调用instance.getSu-perValue()会经历三个搜索步骤:

  1. 搜索实例;
  2. 搜索Sub-Type.prototype;
  3. 搜索SuperType.prototype,最后一步才会找到该方法。

原型链的最顶层

所有函数的默认原型都是Object的实例,因此默认原型都会包含一个内部指针,指向Object.prototype。

继承关系图(完整版):

SubType继承了SuperType,而SuperType了继承Object。当调用instance.toString()时,实际上调用的是保存在Object.prototype中的那个方法。

确定原型和实例的关系

instanceof操作符和isPrototypeOf()方法

//instanceof操作符
console.log(instance instanceof Object);  //true
console.log(instance instanceof SuperType); //true
consolo.log(instance instanceof SubType);  //true

//isPrototypeOf()方法
console.log(Object.prototype.isPrototypeOf(instance));  //true
console.log(SuperType.prototype.isPrototypeOf(instance));  //true
console.log(SubType.prototype.isPrototypeOf(instance));  //true

子类型定义方法注意

  1. 重写父类方法或定义新方法注意:给原型添加方法的代码一定要放在替换原型的语句之后为什么?因为,继承的本质就是重写子类的prototype,如果写在继承前面,那么后面创建实例的时候就访问不到新添加或修改的方法了。
  2. 在通过原型链实现继承时,不能使用对象字面量创建原型方法。
function SuperType() {
    this.property = true;
}

SuperType.prototype.getSuperValue = function() {
    return this.property;
};

function SubType() {
    this.subproperty = false;
}

//继承SuperType,这个在前
SubType.prototype = new SuperType();

//子类添加父类没有的方法,在后
SubType.prototype.getSubValue = function() {
    return this.subproperty;
};

//重写父类方法,在后
SubType.prototype.getSuperValue = function() {
    return false;
};

var instance = new SubType();
console.log(instance.getSuperValue());  //false

var instanceSuper = new SuperType();
console.log(instanceSuper.getSuperValue());  //true
 

由上述结果可以看出:子类重写父类方法,只是屏蔽,原方法不变。

function SuperType() {
    this.property = true;
}

SuperType.prototype.getSuperValue = function() {
    return this.property;
};

function SubType() {
    this.subproperty = false;
}

//继承
SubType.prototype = new SuperType();

//字面量定义方法会使上一句代码无效
SubType.prototype = {
    getSubValue: function() {
        return this.subproperty;
    },

    someOtherMethod: function() {
        return false;
    }
};

var instance = new SubType();
console.log(instance.getSuperValue());  //error
 

上述继承后字面量写原型对象的方法会导致下面的错误是因为:现在的原型包含的是一个Object的实例,而非SuperType的实例,因此我们设想中的原型链已经被切断,现在的SubType和SuperType没关系了。

原型链问题

  1. 包含引用类型值的原型;
  2. 在创建子类型的实例时,不能向超类型的构造函数中传递参数。
//第一个问题

function SuperType() {
    this.colors = ["blue", "red", "yellow"];
}

function SubType() {
}

//继承
SubType.prototype = new SuperType();

var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors);  //"blue,red,yellow,black"

var instance2 = new SubType();
console.log(instance2.colors);  //"blue,red,yellow,black"
 

instance2的colors输出和instance1的colors一样,是因为这属性不是他们自己的,是SuperType实例的,不是他们本身的。

你可能感兴趣的:(JavaScript,ECMAScript)