關於js的原型鏈,__proto__,prototype的理解
首先聲明參考博文,感謝
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的原型對象,該屬性會變成一個對象,其中
__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的理解