一个有关原型的问题牵扯出的问题
最近推荐一个朋友学习js,今天他问了我一个问题,问题如下:
1 function Box (){} 2 Box.prototype = { 3 name : 'Lee', 4 age : 28, 5 run : function (){ 6 return this.name+this.age+'运行中...'; 7 } 8 }; 9 var box = new Box(); 10 Box.prototype = { 11 age : 200 12 }; 13 alert(box.age);
这里alert出来的结果是多少呢?结果是28,他问我为什么是28,我运行代码之后发现是28,但是我也没有想明白为什么是。虽然一直使用js,但是每次遇到原型这个地方的问题的时候我还是会蒙,一直对这个理解的不是很透彻。刚好借着这个问题,我详细的看了下书和资料,算是弄明白了为什么。
这里先引用一段JavaScript高级程序设计(第三版)中的一段话。
无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性。这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。
这里有点抽象,我借用书中一个图说明下:
这里可以看出Box.prototype.constructor == Box;
上面的问题在哪里呢?当Box.prototype = {};一个新的对象的时候,这段代码:
1 function Box (){} 2 Box.prototype = { 3 name : 'Lee', 4 age : 28, 5 run : function (){ 6 return this.name+this.age+'运行中...'; 7 } 8 };
这段之后,这个Box.prototype.constructor == Box;就不成立了,
其实上面说的这段没啥用,只是为了凑字数显得字多,而接下来的new这个操作才是真正的起了作用。这里要理解new这个操作到底做了什么,在理解之前,先插入一段书上的原话:
创建了自定义的构造函数之后,其原型对象默认只会去的constructor属性,至于其他方法,则都是从Object继承而来的。当调用构造函数创建一个新实例后,该实例内部将包含一个指针(内部属性),指向构造函数的原型对象。ECMA-262第五版中管这个指针叫[[Prototype]]。虽然在脚本中没有标准的方式访问[[Prototype]]。但Firefox, Safari和Chrome在每个对象上都支持一个属性__proto__;而在其他实现中,这个属性对脚本则是完全不可见的。不过,要明确的真正重要的一点就是,这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间。
几把越看越晕了,看代码就好了:
1 var box = new Box(); 2 // function new () { 3 // var box = {}; 4 // box.__proto__ = Box.prototype; 5 // Box.apply(box, arguments); 6 // return box; 7 // } 8 注释部分只是方便理解
如果这样的话,上面的问题就一下子晴朗了,因为在new的时候指定了box内部__proto__的指向,这个指向就是Box.prototype的指向,
为了方便理解我把代码修改成如下:
1 function Box (){} 2 3 console.log(Box.prototype.constructor == Box); //true 4 5 var obj1 = { 6 name : 'Lee', 7 age : 28, 8 run : function (){ 9 return this.name+this.age+'运行中...'; 10 } 11 }; 12 13 Box.prototype = obj1; 14 15 var box = new Box(); 16 // function new () { 17 // var box = {}; 18 // box.__proto__ = Box.prototype; 19 // Box.apply(box, arguments); 20 // return box; 21 // } 22 23 console.log(box.age); 24 25 26 var obj2 = { 27 age : 200 28 }; 29 30 Box.prototype = obj2; 31 32 console.log(Box.prototype.isPrototypeOf(box)); //false 33 console.log(Object.getPrototypeOf(box) == Box.prototype); //false 34 console.log(Object.getPrototypeOf(box) == obj1); //true 35 console.log(Object.getPrototypeOf(box) == obj2); //false 36 37 console.log(box.age);
因为已经new过了,而new过之后修改Box.prototype又是直接给了一个新的对象obj2, 所以原来的box内部的__proto__还是指向obj1, 跟Box没关系,只是Box.prototype是指向obj2而已。
一个小小的问题让我看了半天,看来对js的理解还是不够透彻。