深入理解__proto__ 、constructor和prototype的关系

 

深入理解__proto__ 、constructor和prototype的关系

 分类:
[html] view plain copy
 
 print?
  1. <!doctype html>  
  2. <html>  
  3. <head>  
  4.     <meta charset="utf-8">  
  5. </head>  
  6. <body>  
  7. <script type="text/javascript">  
  8. /**  
  9.   
  10. @authors Benjamin  
  11. @date    2013-11-12 10:23:40  
  12. 深入理解javascript 原型和原型链  
  13.   
  14. 最近不是很忙,空余时间整理最近几天看到的关于原型和原型链的文章,收获还是不小的。  
  15. 今天十八届三中全会即将闭幕,期待新一届领导班子的改革方略,能够惠民吧  
  16. 房价,户籍都是让80后很蛋疼的问题哈,当然,做为屌丝的我,肯定也是。  
  17. 开篇先了解下两个很有用的属性:  
  18.   
  19. 一、__proto__属性:  
  20.   
  21. __proto__属性未来会成为ES6标准的一部分,目前,该属性在各个浏览器下的实现差别也许比较大.Firefox是最先实现的这个魔法属性(magic property)的浏览器,而且该属性在Firefox中的表现也最有望能成为标准.我们通常用的__proto__属性都是从Object.prototype上继承下来的,那到底__proto__与prototype什么关系呢?  
  22.   
  23. 1)所有构造器/函数的__proto__都指向Function.prototype,它是一个空函数(Empty function)  
  24. 看看实例:  
  25.   
  26. */   
  27. console.log(Number.__proto__   === Function.prototype) // true  
  28. console.log(Boolean.__proto__  === Function.prototype) // true  
  29. console.log(String.__proto__   === Function.prototype) // true  
  30. console.log(Object.__proto__   === Function.prototype) // true  
  31. console.log(Function.__proto__ === Function.prototype) // true   
  32. console.log(Array.__proto__    === Function.prototype) // true  
  33. console.log(RegExp.__proto__   === Function.prototype) // true  
  34. console.log(Error.__proto__    === Function.prototype) // true  
  35. console.log(Date.__proto__     === Function.prototype) // true  
  36.   
  37. /*  
  38. JavaScript中有内置(build-in)构造器/对象共计12个(ES5中新加了JSON),这里列举了可访问的8个构造器。剩下如Global不能直接访问,Arguments仅在函数调用时由JS引擎创建,Math,JSON是以对象形式存在的,无需new。它们的__proto__是Object.prototype。如下  
  39. */  
  40.   
  41. console.log("对象测试...");  
  42. console.log(Math.__proto__ === Object.prototype);//true  
  43. console.log(JSON.__proto__ === Object.prototype);//true  
  44.   
  45. /**  
  46.  * 上面说的“所有构造器/函数”当然包括自定义的。看下面的例子:  
  47.  */  
  48.   
  49. var Employee = function (){  
  50.   
  51. };  
  52.   
  53. function Person(){  
  54.   
  55. }  
  56. console.log("自定义函数测试...");  
  57. console.log(Employee.__proto__  === Function.prototype);//true  
  58. console.log(Person.__proto__ === Function.prototype);//true  
  59.   
  60. /**  
  61.  * 由以上测试得出,所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身。所有构造器都继承了Function.prototype的属性及方法。如length、call、apply、bind(ES5)。另,Function.prototype也是唯一一个typeof XXX.prototype为 “function”的prototype。其它的构造器的prototype都是一个对象,下面来测试下:  
  62. */  
  63.   
  64. console.log("类型测试中...");  
  65. console.log(typeof Function.prototype) // function  
  66. console.log(typeof Object.prototype)   // object  
  67. console.log(typeof Number.prototype)   // object  
  68. console.log(typeof Boolean.prototype)  // object  
  69. console.log(typeof String.prototype)   // object  
  70. console.log(typeof Array.prototype)    // object  
  71. console.log(typeof RegExp.prototype)   // object  
  72. console.log(typeof Error.prototype)    // object  
  73. console.log(typeof Date.prototype)     // object  
  74. console.log(typeof Object.prototype)   // object  
  75.   
  76. /**  
  77.  * Function.prototype 是一个空函数,我们来测试下  
  78.  */  
  79.   
  80. console.log("空函数测试...");  
  81. console.log(Function.prototype);//function(){}  
  82.   
  83. //用alert来看会更直接点  
  84. //alert(Function.prototype);//function(){}  
  85.   
  86. /**  
  87.  * 下面我们来看看Function.prototype 的__proto__是谁呢?看下面的测试  
  88.  */  
  89.   
  90. console.log("Function.prototype的__proto__测试...");  
  91. console.log(Function.prototype.__proto__ === Object.prototype);//true  
  92.   
  93. /**  
  94.  * 那Object.prototype的__proto__又是谁呢?看下面测试  
  95.  */  
  96.   
  97. console.log(Object.prototype.__proto__);//输出null  
  98.   
  99. /*  
  100. 2)所有对象的__proto__都指向其构造器的prototype  
  101. 来看看以下实例  
  102. */  
  103.   
  104. console.log("对象的__proto__测试中...");  
  105.   
  106. //函数声明  
  107. function Foo(){  
  108.   
  109. }  
  110. var foo = new Foo();//对象实例化  
  111.   
  112. console.log(foo.__proto__ === Foo.prototype);//true  
  113.   
  114. //函数表达式  
  115. var Foo = function (){}  
  116. var foo = new Foo();//对象实例化  
  117. console.log(foo.__proto__ === Foo.prototype);//true  
  118.   
  119. /**  
  120.  * 看完自定义函数,下面我们来看看javascript引擎内置构造器  
  121.  */  
  122.   
  123. var   
  124. arr = ["aaa", "bbb"],  
  125. reg = /^abc$/g,  
  126. obj = {  
  127.     name: "张三",  
  128.     age: 20  
  129. },  
  130. date = new Date(),  
  131. error = new Error("fdasfdasfd");  
  132.   
  133. console.log("内置构造器的对象测试...");  
  134. console.log(arr.__proto__ === Array.prototype);//true  
  135. console.log(obj.__proto__ === Object.prototype);//true  
  136. console.log(reg.__proto__ === RegExp.prototype);//true  
  137. console.log(date.__proto__ === Date.prototype);//true  
  138. console.log(error.__proto__ === Error.prototype);//true  
  139.   
  140. /**  
  141.  * 二、constuctor属性  
  142.  * constructor属性始终指向创建当前对象的构造函数,看下面的实例  
  143.  */  
  144. var   
  145. arr = ["aaa", "bbb"],  
  146. reg = /^abc$/g,  
  147. obj = {  
  148.     name: "张三",  
  149.     age: 20  
  150. },  
  151. date = new Date(),  
  152. error = new Error("fdasfdasfd");  
  153.   
  154. console.log("constructor指向测试...");  
  155. console.log(arr.constructor === Array);//true  
  156. console.log(obj.constructor === Object);//true  
  157. console.log(reg.constructor === RegExp);//true  
  158. console.log(date.constructor === Date);//true  
  159. console.log(error.constructor === Error);//true  
  160.   
  161. /**  
  162.  * 我们都知道函数是对象,对象都有prototype属性,那么这个prototype的constructor属性指向谁呢?  
  163.  * 由以下实例得出结论,其constructor指也向其构造函数  
  164.  */  
  165. console.log("prototype的constructor属性指向测试...");  
  166. //函数声明  
  167. function Foo(){  
  168.   
  169. }  
  170. console.log(Foo.prototype.constructor === Foo);//true  
  171. //函数表达式  
  172. var Foo = function (){  
  173.   
  174. };  
  175. console.log(Foo.prototype.constructor === Foo);  
  176.   
  177. /**  
  178.  * 那么一个函数的实例指向谁呢?  
  179.  */  
  180. function Foo(){  
  181.   
  182. }  
  183. var f1 = new Foo();  
  184. console.log("测试constructor实例化指向...");  
  185. console.log(f1.constructor === Foo);  
  186. console.log(f1.constructor.prototype.constructor === Foo);  
  187.   
  188. /**  
  189.  * 到此我们来必须来总结下子了:  
  190.  * f1 = new Foo();  
  191.  * f1.__proto__ === Foo.prototype.__proto__ === Object.prototype  
  192.  * Foo.prototype.constructor === Foo  
  193.  * Foo.__proto__ === Function.prototype.__proto__ === Object.prototype  
  194.  * 这样看起来貌似还是比较乱,下面用一张图来总结以上问题,如下图:  
  195.  */  

[html] view plain copy
 
 print?
  1. /**  
  2.  * 开一个怪例子,当我们重定义prototype对象时,constructor的行为会有什么变化呢?  
  3.  */  
  4. function Foo(name){  
  5.     this.name = name;  
  6. }  
  7.   
  8. Foo.prototype = {  
  9.     getName :function (){  
  10.         return this.name;  
  11.     }  
  12. };  
  13. var f1 = new Foo();  
  14. console.log("重定义prototype对象测试...");  
  15. console.log(f1.constructor === Foo); //false  
  16. console.log(Foo.prototype.constructor === Foo);//false  
  17. console.log(f1.constructor === Object);//true  
  18. console.log(Foo.prototype.constructor === Object);//true  
  19. /**  
  20.  * 是什么原因导致的此问题呢?来分析哈子,其实定义Foo.prototype = {}  
  21.  * 其实等价于Foo.prototype = new Object({"getName":function(){return this.name;}})  
  22.  * 因此Foo.prototype.constuctor === Obeject 返回true  
  23.  * 那么怎么让前面的两个false 变为true,做如下操作:  
  24.  */  
  25. Foo.prototype.constructor = Foo;  
  26. var f1 = new Foo();  
  27. console.log("重新指向测试...");  
  28. console.log(f1.constructor === Foo); //true  
  29. console.log(Foo.prototype.constructor === Foo);//true  
  30.   
  31. </script>  
  32. </body>  
  33. </html>  
___________________________________________________________________________________________________
 
 

在javascript的使用过程中,constructor 和prototype这两个概念是相当重要的,深入的理解这两个概念对理解js的一些核心概念非常的重要。

我们在定义函数的时候,函数定义的时候函数本身就会默认有一个prototype的属性,而我们如果用new 运算符来生成一个对象的时候就没有prototype属性。我们来看一个例子,来说明这个

复制代码
function a(c){
this.b = c;
this.d =function(){
alert(this.b);
}
}
var obj = new a('test');
alert(typeof obj.prototype);//undefine
alert(typeof a.prototype);//object
复制代码

从上面的例子可以看出函数的prototype 属性又指向了一个对象,这个对象就是prototype对象,请看下图

a.prototype 包含了2个属性,一个是constructor ,另外一个是__proto__

这个constructor  就是我们的构造函数a,这个也很容易理解。

那么__proto__ 是什么呢?

这个就涉及到了原型链的概念:

  每个对象都会在其内部初始化一个属性,就是__proto__,当我们访问一个对象的属性 时,如果这个对象内部不存在这个属性,那么他就会去__proto__里找这个属性,这个__proto__又会有自己的__proto__,于是就这样 一直找下去。

请看mozzlia 对它对它的描述

When an object is created, its __proto__ property is set to constructing function's prototype property. For example var fred = new Employee(); will cause fred.__proto__ = Employee.prototype;.

This is used at runtime to look up properties which are not declared in the object directly. E.g. when fred.doSomething() is executed and fred does not contain adoSomethingfred.__proto__ is checked, which points to Employee.prototype, which contains a doSomething, i.e. fred.__proto__.doSomething() is invoked.

Note that __proto__ is a property of the instances, whereas prototype is a property of their constructor functions.

不管你信不信,我们来看图

在后面如果加上 alert(obj.__proto__ === a.prototype) //true

同理,在这里我们来分析出new 运算符做了那些事情

  1.  var obj={}; 也就是说,初始化一个对象obj。
  2. obj.__proto__=a.prototype;
  3.  a.call(obj);也就是说构造obj,也可以称之为初始化obj。

我们将这个例子改造一些,变得复杂一点。

复制代码
function a(c){
this.b = c;
this.d =function(){
alert(this.b);
}
}
a.prototype.test = function(){
alert(this.b);
}
var obj = function (){}
obj.prototype = new a('test');
obj.prototype.test1 =function(){
alert(22222);
}
var t = new obj('test');
t.test();//alert('test');
复制代码

我们来分析下这个过程

由 var t = new obj('test'); 我们可以得到 t.__proto__ = obj.prototype,但是上面指定obj.prototype =new a('test'); 可以这样来看下

obj.prototype = p, p = new a('test'); p.__proto__ = a.prototype; 

那么obj.prototype.__proto__ = a.prototype,由 t.__proto__ = obj.prototype 可以得出 t.__proto__.__proto__ = a.prototype,

所以对象t先去找本身是的prototype 是否有test函数,发现没有,结果再往上级找,即 t.__proto__   ,亦即obj.prototype 寻找test函数 ,但是obj.prototype 也没有这个函数,然后再往上找。即

 t.__proto__.__proto__ 找,由于t.__proto__.__proto__ = a.prototype  在 a.prototype  中找到了这个方法,输出了alert('test')

从这里可以分析得出一个结论,js中原形链的本质在于 __proto__ 

再看看一个列子

复制代码
function a(c){
this.b = c;
this.d =function(){
alert(this.b);
}
}
var obj = new a('test');
alert(obj.constructor);//function a(){}
alert(a.prototype.constructor);//function a(){}
复制代码

根据上面讲到的__proto__ 我们来分析下,首先obj是没有constructor 这个属性的,但是 obj.__proto__ = a.prototype;就从

a.prototype中寻找,而 a.prototype.constructor 是就a,所有两者的结果是一一样的.

 
posted @ 2016-10-26 20:02  propheterLiu  阅读(558)  评论(0编辑  收藏  举报