1. 程式人生 > >關於js的原型鏈,__proto__,prototype的理解

關於js的原型鏈,__proto__,prototype的理解

ava 對象 構造 script .com 自己的 pan 需要 defined

首先聲明參考博文,感謝

http://blog.sina.com.cn/s/blog_6c62f4330102wq0u.html

http://blog.csdn.net/leadn/article/details/51781539

等作者,還有一些其他鏈接這裏就不粘貼了

js中關於prototype的文章不少,今天自己寫一點自己的理解

不喜歡上來就粘概念,因為如果概念能夠理解,也不需要寫這篇文章了。

關於原型鏈裏面的東西,主要就是prototype,__proto__這兩個屬性難以理解,今天我們就來看一下怎麽玩耍這兩個屬性

先上代碼

:先用js自定義一個對象,同時創建這個對象

  function Man(){   // 定義一個對象

  }
  var m = new Man() ; //創建一個對象
ok,到此我們的原材料就準備好了 接下來,不就是兩個屬性麽,我們都來測試一下就好了 代碼要是拼接字符串打印的效果不好,所以直接打印
console.log(m.__proto__)   //對象的__proto__屬性
console.log(m.prototype) //對象的prototype屬性
console.log("========================")
console.log(Man.__proto__) //函數的__proto__屬性
console.log(Man.prototype) //函數的prototype屬性

 結果

技術分享圖片

我們就直接直觀的看結果就好了

  1.明顯的m.prototype打印為undefined

  2.m.__proto__和Man.prototype打印的結果是一樣的,可以自己手動證明(m.__proto__ == Man.prototype 返回 true)

  3.還有一個Man.__proto__打印的東西好奇怪 function() ,無所謂,暫時不管他

這個時候該上概念了

   prototype是構造函數的屬性,指的就是構造函數的原型在生成實例的時候,js會根據構造函數的prototype屬性將該屬性下的對象生成為父類,只有構造函數這個屬性才有這種效果哦~如果一個構造函數沒有指定該屬性,那麽該屬性下的__proto__會默認的指向原生Object的原型對象,該屬性會變成一個對象,其中

constructor屬性指向本身。(這句直接粘來的,暫時看不懂就看我標紅就行)

__proto__是對象具有屬性可稱為隱式原型,一個對象的隱式原型指向構造該對象的構造函數的原型,這也保證了實例能夠訪問在構造函數原型中定義的屬性和方法。

  開始解釋

    1.為什麽m.prototype打印為undefined?

      廢話,prototype是構造函數的屬性,m是個對象,自然沒有這個屬性了,所以打印undefined

    2.m.__proto__和Man.prototype打印的結果是一樣的

      他倆其實說的就是一個對象,看上面的藍字,都是指向原型,我這裏用的同一個對象,自然相等啊

    3.不解釋,等著

那麽這兩個東西怎麽用呢?

  你再看概念,prototype是構造函數的屬性,__proto__是對象具有屬性

  其實,只要區分清楚函數和對象,就能調用對應的屬性,達到你想要的結果

  技術分享圖片

  別跟我說函數是對象,容易混淆,函數就是函數

  到此,你只要能夠知道的這麽兩點就行了

    1.只有函數可以調用prototype屬性

    2.對象可以調用__proto__,但是不能調用prototype屬性

  接下來說原型鏈

   其實,你只要理解了上面的兩點,原型鏈就簡單多了

   開始

   先回顧上面概念:看上面綠字(在生成實例的時候,js會根據構造函數的prototype屬性將該屬性下的對象生成為父類),簡單講,就是函數的prototype屬性指向了他爸爸。

   
  再來看prototype的字面意思:原型,啥意思呢,就是說對象在創建之初,是根據原型的樣子來創造的(就像女媧造人,照著自己的樣子造,也可以理解為照著圖紙造大樓)
,也就是說原型是誰,他就象誰。

  那麽,上面的Man.prototype怎麽指向了自己的函數對象呢?
     沒有指定過prototype的函數就像孤兒一樣,沒有爸爸,怎麽辦呢,自己就是自己的爸爸(自己照顧自己唄),既然不能長得像別人,那就像自己好了

  原型鏈,要想鏈起來肯定得有多個對象啊,(其實所有的對象都來自Object,這裏怕引起誤會,就不用Object了)
  我們再聲明一個對象People
    function People(){
        this.name = "Bullet";
    }

   然後讓people成為Man的爸爸(這裏就是實現js的繼承,這種繼承代碼不夠健壯,可以自己百度其他的繼承方式,但這也算一種繼承方式)

   很簡單,不是說函數的prototype屬性指向了他爸爸麽,那麽我就

  

   Man.prototype = new People(); //這裏也只能用Man.prototype,因為對象是沒有prototype屬性的,這裏後面要用new 是因為prototype指向的是一個對象哦

    這樣,構造Man的時候就不照著Man構造了,而是照著People構造,既然照著People構造,People有的Man也得有啊。這樣就實現了繼承

    

  (測試繼承的代碼)

        function People(){
            this.name = "Bullet";
        }
        

        function Man(){

        }
        Man.prototype = new People(); //讓Man認People做爸爸
var m = new Man(); console.log(m.name); //這裏就能繼承父類的name屬性了,會打印Bullet

  

    那麽,有了繼承,如果子類的屬性沒有自定義的話,就會使用父類的屬性,(就像這裏可以讀到父類的name屬性一樣)合情合理啊,但是Man到底怎麽找到People的name屬性的呢

    這就要循著原型鏈尋找了

    我們這裏要找m的name屬性,m本身是沒有name的,怎麽辦?找他爸爸要啊,可他爸爸怎麽找呢?我們只設定了Man.prototype = new People();,但是我m是個對象,有沒有prototype屬性,怎麽辦呢?

    還能怎麽辦?,我是個對象,我雖然沒有prototype但是我有__proto__屬性啊

    m.__proto__ == Man.prototype

    反之亦然啊

    Man.prototype = 爸爸對象
    m.__proto__ == Man.prototype
    m.__proto__ = 爸爸對象

    恭喜m對象順利找到爸爸!!!這樣就可以用爸爸的屬性了呦

    結論

      所以子類找父類就像這樣

      首先我們根據prototype 來實現繼承,但是子類找父類時卻要通過__proto__屬性。

      沒辦法子類沒有prototype啊,你能拿我怎麽樣?

      子對象.__proto__這樣就找到了父類,

      原來__proto__屬性就是對象用來找爸爸的屬性啊!!!

    

      

      接著,找到的父類是個對象啊,就能繼續__proto__找到爺爺,一致這麽找下去

      所以 對象.__proto__.__proto__.__proto__.__proto__....是能一直點到盡頭的(null)

      這個.__proto__.__proto__.__proto__.__proto__....組成的就是一條原型鏈

      

      這時,有人會有疑問

        函數.prototype.prototype...可以一直點麽

      肯定不行啊,都說了 函數.prototype 獲取(或設置)的是個對象,對象沒有prototype屬性,所以點到第二層就已經掛掉了(undefined

      

      console.log(Man.prototype.prototype);  // 這裏一定為undefined  Man.prototype指向的是一個對象,對象沒有prototype屬性

      這也又一次解釋了問題一

        1.為什麽m.prototype打印為undefined?

            廢話,prototype是構造函數的屬性,m是個對象,自然沒有這個屬性了,所以打印undefined

      兩者可以結合,我們可以

      函數.prototype.__proto__.__proto__.__proto__....是能一直點到盡頭的,最後為null

 最後,我們解釋最後一個問題

    3.為什麽Man.__proto__打印的東西好奇怪 ,是一個function()

      首先他不是undifined,說明函數是有__proto__這個屬性 ,怎麽強行解釋一波呢?

      很簡單,我先道個歉,對不起,因為函數他確實是個對象,剛才讓你區分是為了好解釋,現在你再想起來吧(因為萬物皆對象,函數當然也是對象)

      所以Man這個函數是可以使用__proto__屬性的

     

    上面又說了__proto__相當於找爸爸的屬性,

    我們使用Man.__proto__返回function() 說明function()是這個函數的爸爸,我們還能再繼續.__proto__,能夠找到Object,說明fanction也是Object的一個子類

        console.log(Man.__proto__);           //找到爸爸function()
        console.log(Man.__proto__.__proto__); //找到爺爺Object{}

    解釋一:

      這裏為什麽不是找到People,原因是因為這裏是一個Man函數對象,函數是通過function()來構造的,上面的繼承關系是對對象(就是m)來講的,

      所以Man函數的爸爸是function,而new Man()對象的爸爸是People,不要混淆對象和函數的區別就好(雖然我知道函數也是個對象)

    解釋二:

      網上經常可以看到這張好亂的對象關系

      技術分享圖片

  你慢慢看原圖,我再給你畫一個(雖然很醜)

  

  技術分享圖片

      使用Object這個超類來找父類的時候會返回null(傳說中的無中生有),可以自行百度,上面參考鏈接中有提到返回null的原因

      

    最後總結

    對象找爸爸用__proto__屬性來找,這個大家都有,萬物皆對象

    函數因為可以構造,所以可以使用prototype來找他是根據誰構造出來的,但是只能找一層,因為函數是根據對象構造出來的,繼續找就只能用__proto__

    所以, 一個對象.__proto__ == 這個對象的函數.prototype

    翻譯成人話就是 對象他爸 == 構造這個對象的對象

    記住只要分清對象和函數兩個東西,使用__proto__和prototype就不會再有難度了

    頭一次寫博客,大家可以隨意噴啊,促進進步,哈哈哈

關於js的原型鏈,__proto__,prototype的理解