Javascript 中的原型链、prototype、__proto__的关系

sywangyc 8年前
   <p><img src="https://simg.open-open.com/show/eeb4942ffd2ef638811fb4d01249c88d.jpg"></p>    <p>上图是本宝宝用Illustrator制作的可视化信息图,希望能帮你理清Javascript对象与__proto__、prototype和原型链之间的关系。如果暂时看不懂也没关系,这篇文章让你从0变成1。</p>    <h3><strong>0、感性认识JS里的“德罗斯特效应”之原型链</strong></h3>    <p>如果你打开浏览器的控制面板,随便输入一个JS内置的构造器函数,比如Array,控制台输出的是一个名为Array的函数体,这好像并没有什么稀奇的,但是,当你接着输入Array.prototype,控制面板输出了一堆我们经常用到的Array构造器的方法,把目光转移到最下方,有一个叫__proto__的属性,好奇的点开,</p>    <p><img src="https://simg.open-open.com/show/17a67eb55b342cf6f860f92bc8a0a397.gif"></p>    <p>咦~?列表列出的不是Object构造器的方法么,里边有我们非常熟悉的hasOwnProperty还有toString等方法。我们常说的一切皆对象,一切对象皆null这么玄妙的编程界哲理和这种嵌套会不会有什么关系呢?更神奇的在下边。</p>    <p><img src="https://simg.open-open.com/show/fbeb4c7ea3b817c2512080c5e3eab554.gif"></p>    <p>继续深入思考,Array是构造器,那么控制台输出的Array.prototype的所有属性中的constructor(翻译:构造器)又是指向什么?点开看看,之后就像身处德罗斯特效应中一样,__proto__和constructor,还有Array构造器中常用的方法名不断的出现,一层套一层,一层层展开,没有尽头。。。</p>    <h3><strong>一、满满一堂证明题课</strong></h3>    <p>当你也发现了JS里这种循环往复、不断嵌套的规律后,仍然不相信自己的眼睛,于是决定亲自证明一下,哦不,是好几下。(对于初学者只靠空想太烧脑,于是本宝宝做了几个GIF动图。)</p>    <p>1、怎么证明你就是你?</p>    <p>这个问题可难不倒JS中的构造器们,一个证明秒秒钟证明“我就是我”,是颜色不一样的烟火。</p>    <p>拿Array举例,Array.prototype中有一个constructor属性,这个属性的值就是Array构造器自己!</p>    <p><img src="https://simg.open-open.com/show/d30acab2bdab01fd96ff6692ae56e981.gif"></p>    <p>2、“遗传进化链__proto__”,怎么证明一切皆对象?</p>    <p>所有的JS内置构造器都本是对象,这要从long long ago说起。可是从什么线索开始向过去前进呢,原型链(我给他起了一个名字叫遗传进化链)就是突破口。所有JS构造器(当然不止构造器有)都有一个__proto__属性,这是原型链指针,指向进化成它的“那个”。(就像我的__proto__指针指向我父母,我父母各自的__proto__指向他们的父母... ...鬼知道最尽头的Ta经历了什么!?(手动黑人问号脸)。在进化的过程中,父母的遗传物质(属性、方法)被我的__proto__指针引用着,同时我也变异出了自己的能力(属性、方法),然后我的子代的__proto__指针又引用我的遗传物质。额!为毛突然莫名脸红~)</p>    <p><img src="https://simg.open-open.com/show/d0521fe878257f1e5144f3c352aa9638.gif"></p>    <p>从上图中发现,JS内置构造器其中之一的Array原本就是一个函数,而这个函数就是Function的prototype,所以Function.prototype有的方法,JS内置构造器都有,比如call()、apply()、bind()等(其实我们自定义的函数也是继承自Function.prototype,所以我们自己也可以定义构造器,创造属于自己的小小王国)。(所以在编程的世界里模拟一个宇宙系统是有可能的,说不定我们本来就生活在被设定好的编程世界里哦。)</p>    <p>而Function.prototype的进化链指针又指向了Object.prototype。</p>    <p>3、怎么证明到头来一切都是空?</p>    <p>不管你从那个属性开始,连续引用__proto__的指针,最后输出的那个值就是null。</p>    <p><img src="https://simg.open-open.com/show/68f9d968420638530a517aaed29d4d56.gif"></p>    <p>上图中,一时手残,打成大写的P了。</p>    <p>万物都是从无生出来的?!Hi boy 老聃的道德经han庄子 读多了吧</p>    <p>4、怎么证明所有JS内置构造器和自定义函数都是Function构造器的原型(prototype)。</p>    <p>不断强化这一认知,实践出真知。</p>    <p>①、String构造器的进化链指针__proto__指向Function构造器的原型</p>    <pre>  <code class="language-javascript">//String构造器  String.__Proto__ === Function.prototype</code></pre>    <p><img src="https://simg.open-open.com/show/a1140c076599078f7049e460a08d3b79.gif"></p>    <p>②、Number构造器的进化链指针__proto__指向Function构造器的原型</p>    <pre>  <code class="language-javascript">//Number构造器  Number.__Proto__ === Function.prototype</code></pre>    <p><img src="https://simg.open-open.com/show/adcedc8a99450accd6a3a6a6622e09b9.gif"></p>    <p>③、Boolean构造器的进化链指针__proto__指向Function构造器的原型</p>    <pre>  <code class="language-javascript">//Boolean构造器  Boolean.__Proto__ === Function.prototype</code></pre>    <p><img src="https://simg.open-open.com/show/606f88d35254c67082d01b542b177283.gif"></p>    <p>④、Array构造器的进化链指针__proto__指向Function构造器的原型</p>    <pre>  <code class="language-javascript">//Array构造器  Array.__Proto__ === Function.prototype</code></pre>    <p><img src="https://simg.open-open.com/show/ad9e2c23e7db0eaa4d9df6981378dd27.gif"></p>    <p>⑤、没错,Function构造器的进化链指针__proto__也指向自己的原型</p>    <pre>  <code class="language-javascript">//Function构造器  Function.__Proto__ === Function.prototype</code></pre>    <p><img src="https://simg.open-open.com/show/8e5e85e79e92d3210d60b1610679f4e0.gif"></p>    <p>⑥、Date构造器的进化链指针__proto__指向Function构造器的原型</p>    <pre>  <code class="language-javascript">//Date构造器  Date.__Proto__ === Function.prototype</code></pre>    <p><img src="https://simg.open-open.com/show/2cfd66def1b1eb9ee9e24cdfc7486fe1.gif"></p>    <p>⑦;、Error构造器的进化链指针__proto__指向Function构造器的原型</p>    <pre>  <code class="language-javascript">//Error构造器  Error.__Proto__ === Function.prototype</code></pre>    <p><img src="https://simg.open-open.com/show/1ab92e20b0a309ae3221a45607649e7a.gif"></p>    <p>⑧、Object构造器的进化链指针__proto__指向Function构造器的原型</p>    <pre>  <code class="language-javascript">//Object构造器  Object.__Proto__ === Function.prototype</code></pre>    <p><img src="https://simg.open-open.com/show/8d1075cc06505969748c345db1963df3.gif"></p>    <p>⑨、RegExp构造器的进化链指针__proto__指向Function构造器的原型</p>    <pre>  <code class="language-javascript">//RegExp构造器  RegExp.__Proto__ === Function.prototype</code></pre>    <p><img src="https://simg.open-open.com/show/175fbdacc8740f41cc21384b026a4166.gif"></p>    <p>⑩、Event构造器的进化链指针__proto__指向Function构造器的原型</p>    <pre>  <code class="language-javascript">//Event构造器  Event.__Proto__ === Function.prototype</code></pre>    <p><img src="https://simg.open-open.com/show/d1d64d9c454b6f8ba64836a5e042ecfc.gif"></p>    <p>这里需要注意所有构造器的prototype都是对象(object)类型,只有Function.prototype是函数(function)类型,这是为了保证函数构造器们的__proto__指向的都是函数。</p>    <p>二,咦?JSON和Math哪去啦?</p>    <p>JS内置的构造器函数都可以使用new关键字实例化一个对象,我们称实例化后的这个对象就是某某构造器的一个实例。就像我们每一个“人”都是“人类”这个构造器函数的一个实例</p>    <pre>  <code class="language-javascript">//实例化一个String构造函数  var str = new String("Hi, today! "); </code></pre>    <p><img src="https://simg.open-open.com/show/e37c0c5980fa7a539b6a99e7213e0f53.gif"></p>    <p>既然上边10个构造器函数都能这样实例化对象,那么JSON和Math是不是也可以用new 关键字实例化呢?试试看!</p>    <pre>  <code class="language-javascript">//尝试实例化JSON和Math两个构造器函数  var json = new JSON(); var Math = new Math(); </code></pre>    <p><img src="https://simg.open-open.com/show/b5c5454b9e0219331d48fd32c1a9cf6e.gif"></p>    <p>哦No,不可以。JSON和Math不是构造器函数,他们是普通的对象。</p>    <p>上边提到过,只有构造器函数才能使用new 关键字实例化一个对象,而JSON和Math已经是对象了,所以我们可以不用实例化直接使用JSON和Math中的属性和方法~~(我们实例化的目就是想用实例化后的对象里的属性和方法,那么既然JSON和Math已经是对象了,就省去实例化的操作喽。当然,能实例化有能实例化的好处~)</p>    <p>所以JSON和Math不属于10个构造器函数,但他们12个共同属于Javascript的内置对象。</p>    <h3><strong>三、__proto__进化链指针设计为什么如此重要!!</strong></h3>    <p>javascript中为什么会有__proto__原型链的设计,不防做一个小实验先。</p>    <pre>  <code class="language-javascript">//实例化一个String对象  var str = new String("Hi!")</code></pre>    <p>我们先实例化一个String对象并将其赋值给str这个变量,然后我们输出这个str</p>    <p><img src="https://simg.open-open.com/show/1466cf33fbdab26cc9b0fe330597f9e9.gif"></p>    <p>从str输出的内容来看,str有四个属性,分别是0、1、2、length</p>    <pre>  <code class="language-javascript">//我们一个个输出这些属性  console.log(str[0]) //H console.log(str[1]) //i console.log(str[2]) //! console.log(str['length']) //3</code></pre>    <p>这毋庸置疑,但是接着往下看。</p>    <pre>  <code class="language-javascript">//charAt()是str对象中不存在的属性方法,但是没有报错,依然可以输出!  str.charAt(0) // 输出'H'</code></pre>    <p>charAt()是str对象中不存在的属性方法,但是没有报错,依然可以输出!</p>    <p><img src="https://simg.open-open.com/show/f9bd1582a8c1098267813a3b969b627c.gif"></p>    <p>这是为什么?这就是进化链__proto__的用处。</p>    <p>str这个对象本身的确没有charAt()这个方法,但是str的进化链上存在这个属性方法,那么charAt()这个方法在进化链的那个节点上呢?</p>    <p><img src="https://simg.open-open.com/show/e22b9b60bdbba230e5f9a8b4a0000e4c.gif"></p>    <p>哦~,原来String.prototype拥有charAt这个方法,而str的__proto__指针指向String.prototype</p>    <p>有小伙伴就问了,“那为什么我爷爷写得一手好毛笔字,可我却没遗传这一点呢?”“呵呵”</p>    <p>这么说的话,str.__proto__.__proto__指向的对象所拥有的属性str也都可以直接用喽?答案是肯定的!</p>    <p><img src="https://simg.open-open.com/show/8d78f6c295c1e5377af124641f7b6b40.gif"></p>    <p>看到str.__proto__.__proto__指向的对象所拥有的属性中有一个hasOwnProperty属性方法了么,str可以直接使用这个属性方法。</p>    <p>在验证之前先说下str.__proto__.__proto__指向了谁?指向的是Object的prototype属性。</p>    <pre>  <code class="language-javascript">str.__proto__.__proto__ === Object.prototype // true</code></pre>    <p>Object.prototype.hasOwnProperty()属性方法用来检验一个对象是否自己拥有一个属性而非通过进化链__proto__继承来的属性。</p>    <p>好的,实验开始:</p>    <pre>  <code class="language-javascript">//检查str是否拥有length属性  str.hasOwnProperty('length') //true //检查str是否拥有0属性(str.charAt(0)的输出是'H') str.hasOwnProperty(0) //true //检查str是否拥有1属性 str.hasOwnProperty(1) //true //检查str是否拥有2属性 str.hasOwnProperty(2) //true //检查str是否拥有3属性(str的length是3,所以索引值从0开始,所以索引最大是2,所以没有3这个属性) str.hasOwnProperty(3) //false //str是否拥有hasOwnProperty这个属性呢?答案是否定的。 str.hasOwnProperty('hasOwnProperty') //false</code></pre>    <p><img src="https://simg.open-open.com/show/1d8a94dfdde44fa9ae6bdd53583c45f6.gif"></p>    <h3><strong>四、结语</strong></h3>    <p>现在再来看这张图,是不是思路清晰多了呢!</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/eeb4942ffd2ef638811fb4d01249c88d.jpg"></p>    <p>当你弄清楚了原型链(我喜欢叫他进化链)__proto__,prototype之间的关系,还有Javascript中12个内置对象,其中10个函数类型,2个对象类型。再来学习这12个内置对象的属性和属性方法是不是如鱼得水,心里跟明镜似的~</p>    <p>对于Javascrip初学者,一时半会肯定还是搞不清楚,唯一的办法就是多看、多想、多模仿、多创新、多总结、多分享~,学习的本质无非就是这些嘛,教育的本质无非就是教会你用学习的本质学习你所感兴趣的嘛。</p>    <p> </p>    <p>来自:http://www.cnblogs.com/libin-1/p/5955208.html</p>    <p> </p>