<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          四張圖帶你搞定原型和原型鏈

          共 5045字,需瀏覽 11分鐘

           ·

          2021-08-31 22:20


          前端獵手
           鏈接每一位開(kāi)發(fā)者,讓編程更有趣兒!
          關(guān)注


          在講原型和原型鏈之前,先鋪墊一些前置知識(shí):

          1. 「所有的對(duì)象都是通過(guò)new 函數(shù)生成的?!?/strong> 包括let obj = {},這種形式其實(shí)是語(yǔ)法糖,本質(zhì)上是通過(guò)let obj = new Object()生成的。那么函數(shù)又是如何生成的呢?從圖中可以清晰的看出函數(shù)本質(zhì)上是通過(guò)new Function生成的,盡管我們平時(shí)不會(huì)這么去寫,當(dāng)然也不建議這么去寫

            function Test(){}
            //相當(dāng)于
            let Test = new Function();

            那么Function函數(shù)又是誰(shuí)生成的呢?Function函數(shù)也是函數(shù),剛剛我們說(shuō)函數(shù)是通過(guò)new Function生成,但它是一種特殊的情況,不通過(guò)任何東西創(chuàng)建,它是JS引擎啟動(dòng)的時(shí)候直接添加到內(nèi)存當(dāng)中的。

          2. 「所有的函數(shù)也都是對(duì)象,既然是對(duì)象,那么函數(shù)一定會(huì)有屬性?!?/strong> 比如:Array.formArray.isArray等等

          3. 「對(duì)象是一種引用類型。」

          ?? 原型 prototype

          所有的函數(shù)都有一個(gè)屬性:prototype,稱之為函數(shù)原型。函數(shù)創(chuàng)建之初就會(huì)自動(dòng)加上prototype屬性。

          那什么是原型呢?

          默認(rèn)情況下,prototype就是一個(gè)普通的object對(duì)象。

          默認(rèn)情況下,prototype中有一個(gè)屬性:constructor,它也是一個(gè)對(duì)象,它指向構(gòu)造函數(shù)本身。

          這張圖很清晰說(shuō)明了prototypeconstructor之間的關(guān)系,每個(gè)函數(shù)(add、Object、Array、nothing)都有一個(gè)屬性prototype,它指向函數(shù)的原型,而函數(shù)的原型中也有一個(gè)屬性constructor,它也是一個(gè)對(duì)象,constructor指向構(gòu)造函數(shù)本身。

          那原型有什么用呢?原型本身沒(méi)什么用,但是配合隱式原型卻大有作為

          ?? 隱式原型  __proto__

          「所有的對(duì)象都有一個(gè)屬性:__proto__,稱之為隱式原型?!?/strong> 前后兩個(gè)下劃線表示系統(tǒng)私有屬性,不要輕易動(dòng)它。

          默認(rèn)情況下,隱式原型指向創(chuàng)建該對(duì)象的函數(shù)的原型。這句話特別重要,它將隱式原型跟原型聯(lián)系起來(lái)了,那什么意思呢?

          舉個(gè)栗子??:

          function Test(){

          }
          let obj = new Test();
          // obj.__proto__ === Test.prototype; 返回true

          舉個(gè)栗子??:

          function Test(){
              return {};// 這里{}是語(yǔ)法糖,本質(zhì)上是通過(guò)new Object()創(chuàng)建的
          }
          let obj = new Test();//由于Test函數(shù)中返回 {},所以new Test()的結(jié)果是 let obj = new Object()
          // obj.__proto__ === Object.prototype; 返回true

          現(xiàn)在我們知道隱式原型指向誰(shuí),然后我們將prototype、constructor__proto__三者關(guān)系繪圖如下:

          在前置知識(shí)中,已經(jīng)說(shuō)過(guò)所有的對(duì)象都是通過(guò)new 函數(shù)進(jìn)行創(chuàng)建的,便有了上圖關(guān)系。細(xì)心的小伙伴應(yīng)該已經(jīng)發(fā)現(xiàn)對(duì)象1的__proto__、對(duì)象2的__proto__以及函數(shù)add的prototype三者指向同一塊內(nèi)存空間,這也就解釋了為什么要把函數(shù)寫在原型上,這是因?yàn)閷⒑瘮?shù)寫在原型上,只要是通過(guò)add構(gòu)造函數(shù)創(chuàng)建的對(duì)象都可以訪問(wèn)這個(gè)函數(shù)。

          function Add(name,age){
                 this.name = name;
                 this.age = age;
           }

          Add.prototype.say = function(){
               console.log("法醫(yī)",this.name,this.age)
          }
          let obj1 = new Add("前端獵手",18);
          let obj2 = new Add("仵作",20);
           
           obj1.say(); //法醫(yī) 前端獵手 18
           obj2.say(); //法醫(yī) 仵作 20

          訪問(wèn)對(duì)象成員的順序是:首先會(huì)看當(dāng)前對(duì)象中是否存在該屬性或者方法,若存在,就直接使用了,否則繼續(xù)順著原型鏈依次查找。

          「MDN文檔中一段話:」

          鏈接查看

          之所以會(huì)繼承Array.prototype就是因?yàn)?code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(145, 109, 213);font-weight: bolder;background-image: none;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;">隱式原型的存在,這也是提示我們,將來(lái)要把對(duì)象需要共享的東西寫在原型上,特別是函數(shù),這種行為有個(gè)比較有意思的名稱,叫猴子補(bǔ)丁,也許是因?yàn)??善于模仿,在原型中加入成員,以增強(qiáng)對(duì)象的功能,但也是有弊端的,會(huì)造成原型污染,所以還是謹(jǐn)慎使用。

          ?? 原型鏈

          這張圖搞清楚后,自然明白何為原型鏈,我們一起過(guò)一遍

          1. 我們先看白色線條,白色線條表示原型,在原型部分我們已經(jīng)說(shuō)了,所有的函數(shù)都有一個(gè)屬性prototype,那么Object函數(shù)的原型指向Object原型,同理,我們自定義函數(shù)的原型必然指向自定義函數(shù)原型,這里有個(gè)比較特殊的點(diǎn),就是Function函數(shù),沒(méi)有任何東西創(chuàng)建它,它是由JS引擎啟動(dòng)的時(shí)候直接添加到內(nèi)存里面的,故Function函數(shù)直接指向Function原型

          2. 再看綠色線條,綠色線條表示new,讀到這里,想必大家都知道所有的函數(shù)都是通過(guò)new Function()創(chuàng)建的,所以Function函數(shù)分別指向Object自定義函數(shù)無(wú)可厚非,圖中有一條線是自定義函數(shù)指向自定義對(duì)象,文章開(kāi)頭已經(jīng)說(shuō)了,所有的對(duì)象都是通過(guò)new 函數(shù)進(jìn)行創(chuàng)建的,代碼表示:

                function Test(){
                
                }
                let obj = new Test();//這里的 obj 可以表示圖中的自定義對(duì)象
          3. 最后再看藍(lán)色線條,藍(lán)色線條表示隱式原型,我在隱式原型那部分也已經(jīng)說(shuō)了,所有的對(duì)象都有一條屬性__proto__,那函數(shù)是對(duì)象吧,那隱式原型指向誰(shuí)呢?隱式原型指向創(chuàng)建該對(duì)象的函數(shù)的原型,要理解這句話,我們必須要知道函數(shù)是誰(shuí)創(chuàng)建的?這個(gè)誰(shuí)的原型是什么?所有的函數(shù)都是Function函數(shù)創(chuàng)建的Function函數(shù)的原型指向Function原型,這是個(gè)特殊點(diǎn),故Object函數(shù)自定義函數(shù)的隱式原型指向Function原型,Function函數(shù)也是函數(shù),由于沒(méi)有誰(shuí)創(chuàng)建它,是被直接添加到內(nèi)存的,它的原型是指向Function原型,這同樣是一個(gè)特殊點(diǎn)。

               function Test(){}
               //相當(dāng)于
               let Test = new Function();
               
               Test.__proto__ === Function.prototype; // true

            我們都知道所有的函數(shù)都有共同的成員,比如call、apply、bind等等,我們并沒(méi)給自定義函數(shù)上加上這些成員,那么為什么可以使用呢?這是因?yàn)樗械暮瘮?shù)的隱式原型指向Function的原型,這些方法都存在于Function的原型上,所以每個(gè)函數(shù)都可以使用這些成員,這就是繼承的效果。

            繼續(xù)來(lái)看,自定義函數(shù)可以通過(guò)new創(chuàng)建自定義對(duì)象,自定義對(duì)象也是對(duì)象,那必然有隱式原型__proto__,指向創(chuàng)建該對(duì)象的函數(shù)原型,所以自定義對(duì)象的隱式原型指向自定義函數(shù)原型,那么自定義函數(shù)原型又指向誰(shuí)呢?不知道大家是否還記得我在原型那部分說(shuō)過(guò)一句話:默認(rèn)情況下,prototype是一個(gè)普通的Object對(duì)象,所以可以認(rèn)為prototype是通過(guò)new Object()創(chuàng)建的,所以prototype是個(gè)對(duì)象,故自定義函數(shù)的prototype的隱式原型指向Object的原型,看代碼:

                function test(){};//自定義函數(shù)
                
                test.prototype.__proto__ === Object.prototype;// true    

            ?? 特殊點(diǎn):Object的原型的隱式原型指向nullObject.prototype.__proto__ === null,返回true

            現(xiàn)在知道什么是原型鏈了吧,自定義對(duì)象的隱式原型指向自定義函數(shù)的原型自定義函數(shù)的原型的隱式原型又指向Object原型,Object原型又指向null,這種鏈?zhǔn)降年P(guān)系就是原型鏈

          自測(cè)題一道:大家可以試著做一下,然后可以根據(jù)最后一張圖進(jìn)行檢查

          function Fayi({}
          Fayi.prototype.camel = function({}

          var u1 = new Fayi();
          var u2 = new Fayi();

          console.log(u1.camel === u2.camel); 
          console.log(Fayi.prototype.constructor);
          console.log(Fayi.prototype === Function.prototype);
          console.log(Fayi.__proto__ === Function.prototype); 
          console.log(Fayi.__proto__ === Function.__proto__); 
          console.log(u1.__proto__ === u2.__proto__);  
          console.log(u1.__proto__ === Fayi.__proto__); 
          console.log(Function.__proto__ === Object.__proto__);
          console.log(Function.prototype.__proto__ === Object.prototype.__proto__);
          console.log(Function.prototype.__proto__ === Object.prototype); 

          ?? 好了, 以上就是我的分享,小伙伴們點(diǎn)個(gè)贊再走吧 ?? 支持一下哦~ ??,我會(huì)更有動(dòng)力的 ??


          瀏覽 13
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    美女操逼视频在线观看 | 色婷婷视屏 | 日本欧美一级片 | 国产一区二区无码午夜久久久豆花av | 久色伊人 |