原型链继承 与 instanceof原理

前面的话

许多的oo语言都支持两种继承方式:接口继承与实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。js只支持实现继承,而且其实现继承主要是依靠原型链来实现的。

原型链继承的本质

原型链继承的本质是重写原型对象,代之是一个新类型的实例.

下列代码中,Fn的新原型不仅有new Fn1()实例上的全部属性和方法,并且还继承了Fn1原型上的属性与方法。

function Fn1() {
    this.property1 = true;
}
Fn1.prototype.getFn1Vlaue = function () {
    return this.property1;
}

function Fn2() {
    this.property2 = false;
}

Fn2.prototype = new Fn1();// Fn1 的实例为Fn2的原型

Fn2.prototype.getFn2Value = function () {// Fn2原型上添加方法
    return this.property2;
}

var instance = new Fn2();// 创建Fn2的实例
console.log(Fn2.prototype);
console.log(instance.__proto__);
console.log(instance.__proto__.__proto__);
console.log(Fn2.prototype.constructor === Fn1);
console.log(instance.constructor)
console.log(instance.getFn1Vlaue());

在这里插入图片描述
instance实例可以访问Fn1中的属性,原因就是因为Fn2继承了Fn1,而继承是通过创建Fn1实例实现的。

Fn2的原型对象包含Fn1实例的所以属性和方法,然后在Fn2的原型对象上又添加了一个getFn2Value方法。

instance是Fn2的实例,所以instance.__proto__与Fn2.prototype返回的对象一样。
instance. __ proto __.__proto__理所当然返回的就是Fn1.prototype。因为Fn2.prototype被重写了为Fn1,所以Fn2.prototype.constructor === Fn1,相应的instance.constructor指向Fn1。

说明:instance.getFn1Vlaue()会经历三个步骤:1) 搜索实例 2)Fn2.prototype 3)Fn1.prototype。在第三步找到这个方法。

instanceof 操作符原理

instanceof操作符用来检测 实例与原型链中出现过的构造函数,结果返回true。

上面的例子

console.log(instance instanceof Fn2);
console.log(instance instanceof Fn1);
console.log(instance instanceof Object);

在这里插入图片描述
instanceof原理就是沿着原型链上一层一层查找,只要instance.[proto…] === instance.constructor.prototype,那么instance instanceof constructor就返回true。

还有一种方法是使用原型对象上的isPrototypeOf()方法,原理一样:

console.log( Fn2.prototype.isPrototypeOf(instance));
console.log( Fn1.prototype.isPrototypeOf(instance));
console.log( Object.prototype.isPrototypeOf(instance));
原型链继承的问题
  • 多个实例对引用类型的操作会被篡改
  • 子类型的原型上的constructor被重写了(上例中的Fn2.prototype.constructor === Fn1)
  • 给子类型原型添加属性和方法必须在替换原型之后
  • 创建子类型实例时无法向父类型的构造函数传参

[问题 1]

    function Fn1() {
        this.colors = ['red', 'blue', 'green'];
    }
    Fn1.prototype.getColors =  function () {
        console.log(this.colors);
    }
    function Fn2() {
        
    }
    Fn2.prototype = new Fn1();// 继承了Fn1

    var instance1 = new Fn2();
    instance1.colors.push('black');
    instance1.getColors();

    var instance2 = new Fn2();
    instance2.getColors();

在这里插入图片描述
构造函数Fn1中定义了一个colors属性,该属性包含一个数组(引用类型)。将Fn1的实例赋给Fn2.prototype,那么Fn1的实例属性colors就变成了Fn2.prototype的属性,又因为原型属性上的引用类型会被所有实例共享,所以多个实例对引用类型的操作会被篡改。

[问题 2]

子类型原型的上的额constructor属性被重写了,执行了Fn2.prototype = new Fn1()后原型被重写.

console.log(Fn2.prototype.constructor === Fn1);// true

解决方法就是重写Fn2.prototype.constructor属性,指向自己的构造函数Fn2

    Fn2.prototype = new Fn1();
    Fn2.prototype.constructor = Fn2;

[问题 3]

给子类型原型添加属性和方法必须在替换原型之后,原因就是因为在Fn2.prototype = new Fn1()之后Fn2.prototype会被覆盖

    function Fn1() {
        this.colors = ['red', 'blue', 'green'];
    }
    Fn1.prototype.getValue=  function () {
        console.log(this.colors);
    }
    function Fn2() {
        
    }
    Fn2.prototype = new Fn1();// 继承了Fn1
    Fn2.prototype.constructor = Fn2;
    // 重写了超类型的getValue方法
    Fn2.prototype.getValue =  function () {
        console.log(this.value);
    }

    var instance = new Fn2();
    instance.value = 'xiaoqi';
    instance.getValue();

    var instance2 = new Fn1();
    instance2.getValue();

在这里插入图片描述
前面说过instance.getValue()会经过三个步骤:1) 搜索实例 2)搜索Fn2.prototype 3)搜索Fn1.prototype,所以结果为“xiaoqi”。但是如果想用Fn1.prototype上的getValue方法,使用Fn1的实例调用getValue方法就可以了。

[注意]: 在Fn2.prototype = new Fn1()之后,想为Fn2.prototype添加新的属性或方法,不能使用对象字面量的方式 添加,这样会重写原型链。

    function Fn1() {
        this.colors = ['red', 'blue', 'green'];
    }
    Fn1.prototype.getColors=  function () {
        console.log(this.colors);
    }
    function Fn2() {
    }
    Fn2.prototype = new Fn1();// 继承了Fn1
    // 使用对象字面量添加新的方法,会导致上面的代码无效。
    Fn2.prototype = {
        getValue() {
            console.log(this.value)
        }
    }
    var instance = new Fn2();
    instance.getColors();

在这里插入图片描述

上面的代码刚刚把Fn1的实例覆给Fn2.prototype,紧接着又将原型替换为一个对象字面量,导致原型被重写, Fn1 与 Fn2之间没有了关系。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值