JAVASCRIPT面向对象的程序设计之原型链继承

原型链继承

继承是OO(Object oriented 面向对象)语言中的一个最为津津乐道的概念。 许多面向对象语言都支持两种继承方式:接口继承和实现继承。接口继承只继承方法签名,而实现继承则是继承实际的方法,例如在Java中通过关键字implements实现接口即对应接口继承, 通过extends关键字继承类即对应的实现继承;但是在Javascript中, 由于函数没有签名, 所以无法实现接口继承,只支持实现继承,而继承则主要是依靠原型链实现(在ES6中,引入了class类的概念,可通过extends关键字继承)。

实现原型链继承的基本模式(本质是重写原型对象):

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

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

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

SubType.prototype = new SuperType();

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

var instance = new SubType();
alert(instance.getSuperValue());   //true

在上面这个例子中, 没用使用SubType的默认提供的原型, 而是给他换了一个新原型(因为原型本身就是一个对象,所以可以这样赋值);因此,SubType的原型实际上就是SuperType的一个实例,所以也就拥有该SuperType实例的所有属性和方法,而且其内部还有一个指针指向了SuperType的原型。此外要注意的是, instance.constructor现在指向的是SuperType,这是因为原来的SubType.prototype中的constructor被重写了的缘故。

该例子的原型链如下:

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

理解:

首先声明了SuperType和SubType构造函数, SuperType构造函数中有属性property, SubType构造函数中有属性subProperty;

此时执行

SubType.prototype = new SuperType();

则是将SuperType的一个实例new SuperType() 赋值给 SubType.prototype, 即重写了SubType的原型对象。因为SuperType的实例包含属性property, 所以SubType的原型对象中会有property属性。又因为实例有一个特性prototype指向SuberType的原型,所以SubType的原型也会有一个prototype特性指向SuperType的原型,而不是指向Object的原型。

此时执行

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

则是在SubType当前的原型上添加一个getSubValue方法, 所以SubType的原型对象中会有getSubValue方法; SubType中的原型如下:

同理, SuperType的原型也是如此。

判断一个实例对象是什么类型可以使用 instanceof操作符:

console.log(instance instanceof Object);      //true
console.log(instance instanceof SuperType);   //true
console.log(instance instanceof SubType);     //true

上面三个语句的执行结果都是true,说明instance 即是 Object,也是SuperType, 同时也是SubType;

和自然界的继承是一样的,就好比: 实例是一只东北虎(SubType), SubType是东北虎, SuperType是老虎, Object是动物, 这一只东北虎,可以称呼为东北虎,也可以称呼为老虎, 也可以称之为动物。 

判断一个实例是否继承自一个原型,可以使用isPrototypeOf()方法:

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

从该例子的原型链图能够直接看出来, 这个三条语句都是正确的。

 

问题:

原型链虽然强大,可以使用它来继承, 但它也存在一些问题。 其中最主要的问题来之包含引用类型值得原型。 因为引用类型保存的实际上是一个地址值,虽然实例不能改变原型对象中引用类型保存的地址值,但是却可以改变地址值所指向的值(地址值就相当于一个门牌号, 而实际的内容则是房子里的东西;门牌号可以不变, 但是却可以往房子搬东西,或者从房子里往外搬东西)。

例:

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

function SubType() {
}

SubType.prototype = new SuperType();

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

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

通过上面可以知道, 在SubType的原型对象中含有 colors 属性, 而 colors 是一个数组; 在Javascript中,数组是引用类型,所以instance1数组进行操作的时候,实际上是通过colors保存的地址值,找到内存中的位置, 然后修改内存中的值, 而当instance2访问colors属性的时候,会通过colors保存的地址值找到内存中对应的值。 因此,instance1修改的colors的值会在instance2中体现。

因为在实际应用中,往往需要一个对象拥有自己独立的属性, 所以通常很少单独使用原型链。

备注: 例子及截图均来自《JAVASCRIPT高级程序设计:第3版》

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值