关于js继承中“prototype模式“的constructor重定向浅析

文章转自:关于js继承中"prototype模式"的constructor重定向浅析_乱闯的java小屁孩的博客-CSDN博客_js继承constructor

近来在看阮一峰的网络日志,确实写得好,非常通俗易懂,对我们这些小白受益匪浅!

JavaScript的继承一直是一大难点,初涉会花费非常多的时间,我也如此。在摸索了许多天之后,逐渐有了一些头绪。

但是关于constructor属性重定向这个问题上,我还是存在疑问。

在测试过程中,发现测试的结果与阮一峰老师的结论有些出入,在此记录。

首先粘上阮一峰老师的这篇博文:http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html

同时为了方便阅读,在此将部分内容摘抄:

1、阮一峰老师的"prototype模式"讲解
现在有一个"动物"对象的构造函数,以及一个"猫"对象的构造函数。
function Animal(){
  this.species = "动物";
}
function Cat(name,color){
  this.name = name;
  this.color = color;
}
要使“猫”继承“动物”,使用prototype模式的话,就要这样:
"猫"的prototype对象,指向一个Animal的实例,那么所有"猫"的实例,就能继承Animal了。
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
var cat1 = new Cat("大毛","黄色");
 
alert(cat1.species); // 动物
代码的第一行,我们将Cat的prototype对象指向一个Animal的实例。
Cat.prototype = new Animal();
它相当于完全删除了prototype 对象原先的值,然后赋予一个新值。但是,第二行又是什么意思呢?
Cat.prototype.constructor = Cat;
原来,任何一个prototype对象都有一个constructor属性,指向它的构造函数。如果没有"Cat.prototype = new Animal();"这一行,Cat.prototype.constructor是指向Cat的;加了这一行以后,Cat.prototype.constructor指向Animal。
alert(Cat.prototype.constructor == Animal); //true
更重要的是,每一个实例也有一个constructor属性,默认调用prototype对象的constructor属性。 (此处文字加粗,是一会讨论的重点)
alert(cat1.constructor == Cat.prototype.constructor); // true
因此,在运行"Cat.prototype = new Animal();"这一行之后,cat1.constructor也指向Animal!
alert(cat1.constructor == Animal); // true
这显然会导致继承链的紊乱(cat1明明是用构造函数Cat生成的),因此我们必须手动纠正,将Cat.prototype对象的constructor值改为Cat。这就是第二行的意思。

这是很重要的一点,编程时务必要遵守。下文都遵循这一点,即如果替换了prototype对象,

o.prototype = {};
那么,下一步必然是为新的prototype对象加上constructor属性,并将这个属性指回原来的构造函数。
o.prototype.constructor = o;
2、关于自己对constructor属性的理解
首先, 阮老师说:”每个实例也有一个constructor属性,默认调用prototype对象的constructor属性“,我在这一点比较疑问。请看下面的代码:
function Animal(){
    this.species = "动物";
  }
var animal = new Animal();
console.info( "constructor" in animal );//true
console.info( animal.hasOwnProperty("constructor") );//false
console.info( animal.__proto__.hasOwnProperty("constructor") );//true
从上面的代码很明显的看出,新new出来的实例是没有constructor属性的,当访问实例的constructor属性时,根据作用域链的原则,会到其prototype对象中去找。
那么其实当访问实例的constructor属性时,实质上访问的是其prototype的constructor。
所以初步得到的结论是: 实例对象并没有自己的constructor属性!

那么,下面阮老师代码的第二行中的constructor到底是谁的呢?
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
上面的代码我们可以如下转换:
var animal = new Animal();
Cat.prototype = animal;
Cat.prototype.constructor = Cat;
此时我们再输出:
console.info("constructor" in animal);//true
console.info( animal.hasOwnProperty("constructor") );//true
console.info( animal.__proto__.hasOwnProperty("constructor") );//true
此时的animal竟然多出了constructor属性(注意:这里的animal一直是一个实例)。
到此,我做了一个大胆的猜测: 当执行Cat.prototype.constructor = Cat时,其实是手动给Cat.prototype指向的实例新增了一个constructor属性。

为了验证这个猜想,我再次用Firefox的Web控制台查看每次输出的animal中的属性,如下:
首先输出单个animal:
function Animal(){
  this.species = "动物";
}
var animal = new Animal();
console.info(animal);
控制台输出数据:

可以看到animal中除了自己的species属性外没有constructor属性。
然后改写阮老师代码:
function Animal(){
    this.species = "动物";
}
function Cat(){
    this.name="大黄";
}
 
var animal = new Animal();
Cat.prototype = animal;
Cat.prototype.constructor = Cat;
 
console.info(animal);
控制台输出数据:

此处可以看到animal多了一个constructor属性,并指向Cat。

由此可论证: constructor属性并非每个实例固有,而是在为了防止继承链混乱而给已成为其他对象的prototype的实例人为加上的。
所以,其实并非阮老师说的有偏差,而是说的不够细致,以使我们这些小白容易掉到坑里。

最后再重新阐述一下阮老师代码中关于constructor重定向的问题。
当令Cat.prototype = animal,然后访问新new出来的实例cat的cat.constructor时,由于cat实例中没有,根据作用域链原则向上查找,查找其prototype(也就是新new出来的animal),而animal中也没有,继续向上查找,找到Animal.prototype,则其返回Animal的构造方法。
这是我们不希望看到的,因为cat是通过Cat构造方法new出来的,而constructor却返回了Animal的构造方法。
则为了避免这种情况,我们将cat的原型Cat.prototype,也就是animal新增一个constructor属性,并将其强制指向Cat,那么一切返回最初我们希望的状态。cat.constructor能很好的反应其是Cat,而非Animal。
————————————————
版权声明:本文为CSDN博主「乱闯的java小屁孩」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013578282/article/details/46595755

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值