文章转自:关于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