前端入门14-JavaScript进阶之继承 (3)

而 b 对象则是在修改了构造函数 prototype 属性后创建的,所以它的原型链就是新的结构了,跟 a 就会有所不同了。这里之所以会输出 Array,是因为 b 的原型是数组对象 B,而数组对象 B 是由 new Array() 创建的,所以 B 继承了 Array.prototype 的 constructor 属性指向了 Array。这也是之前有说过,不建议手动修改原型链结构,否则会破坏默认的构造函数、原型、实例对象三者间的关系。

如果对原型和构造函数的概念还不是很理解,那么我们换个方式验证:

验证

instanceof 表示如果左边的对象是继承自右边构造函数的 prototype 的话,表达式为 true。

isPrototypeOf 表示,左边的对象如果在右边对象的原型链上的话,表达式为 true。

所以,修改构造函数的 prototype 属性,并不会对原本从构造函数创建的对象的原型链,继承结构有所影响。这其实也再次验证,构造函数在 JavaScript 中的角色类似于作为第三方牵手原型和实例对象,修改原型会影响实例对象,但修改构造函数并不会对原本的实例对象有何影响。

但构造函数之后创建的对象,新对象的继承结构跟之前的就不一样了。

修改对象的 __proto__ 属性

对象有办法直接获取到它的原型对象,一种是通过 __proto__,这是通用方式,所有对象都有,唯一的弊端在于 ES5 中并不是标准规范中的属性,虽然基本所有浏览器中都有实现,所以在一些开发工具中可能不会提示对象含有这个属性。

另一种获取对象原型的方式是,通过 constructor 的 prototype,这也是通用方式,弊端在于,对象的 constructor 属性可能指向的并不是创建它的构造函数,因为这个属性其实是继承自原型对象的属性,所以关键还取决于原型和构造函数之间是否满足默认的相互引用关系。另外,有些对象可能并没有 constructor 属性。

既然对象有属性是指向它的原型,那么手动修改这个属性的指向,会有怎样的影响?

var B = {num:0} //定义一个对象,含有 num 属性 function A() {} //定义一个构造函数 A.prototype.num = 222; //为构造函数prototype添加一个 num 属性 var a = new A(); a.num; //应该输出什么 a.__proto__ = B; //手动修改了 a 对象的原型对象 a.num; //此时应该输出什么 var b = new A(); //b对象跟 a 对象一样通过构造函数 A 创建 b.num; //这里又应该输出什么

a 对象刚被创建来时,是继承的构造函数 A.prototype,所以第一次 a.num 输出 A.prototype.num 的值:222,这里应该没疑问。

然后手动修改了对象 a 的原型,让它的原型指向了 B 对象,那么此时对象 a 的原型链会发生变化吗?它的继承结构会发生变化吗?测试一下:

前端入门14-JavaScript进阶之继承

所以,手动修改对象的 __proto__ 属性是会影响到对象的原型链的,虽然对象在创建时会根据构造函数的 prototype 生成一条原型链,但运行期间,手动修改对象的原型指向,会重新让对象推翻原本原型链,再重新生成一条新的原型链的。

那么,会影响到之后构造函数创建的新对象的原型链吗?测试一下:

前端入门14-JavaScript进阶之继承

所以,手动修改某个对象的原型指向,只会让这个对象的原型链重建,并不会影响到创建它的构造函数之后创建的新对象的继承关系。

最后来小结一下:

在 JavaScript 中,由于对象继承自原型,但原型本质上也是对象,所以,如果在运行期间动态修改原型对象上的属性,会影响到继承它的子对象们读取相关原型属性的结果。

由于继承关系通常是在构造函数创建新对象时,由构造函数的 prototype 属性值决定,而构造函数本质上也是对象,也可在运行期间,动态修改属性值。但如果运行期间,手动修改构造函数的 prototype 属性值,并不会影响到原先通过该构造函数创建的对象的继承结构(原型链),但之后通过该构造函数创建的新对象的继承结构(原型链)就跟之前的不一样了。

也就是即使同一个构造函数,但如果有修改过构造函数的 prototype 指向,那么该构造函数前后创建的对象的继承结构(原型链)也是会不一样的。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxwsw.html