詳解js中的原型鏈,prototype與__proto__的關系

說到prototype,就不得不先說下new的過程。

我們先看看這樣一段代碼:

<script type="text/javascript">
        var Person = function () { };
        var p = new Person();
</script>

很簡單的一段代碼,我們來看看這個new究竟做了什么?我們可以把new的過程拆分成以下三步:

  1. var p={}; 也就是說,初始化一個對象p。

  2. p.__proto__=Person.prototype;

  3. Person.call(p);也就是說構造p,也可以稱之為初始化p。
    關鍵在于第二步,我們來證明一下:

<script type="text/javascript">
    var Person = function () { };
    var p = new Person();
    alert(p.__proto__ === Person.prototype);
</script>

這段代碼會返回true。說明我們步驟2的正確。

那么__proto__是什么?我們在這里簡單地說下。每個對象都會在其內部初始化一個屬性,就是__proto__,當我們訪問一個對象的屬性 時,如果這個對象內部不存在這個屬性,那么他就會去__proto__里找這個屬性,這個__proto__又會有自己的__proto__,于是就這樣 一直找下去,也就是我們平時所說的原型鏈的概念。

按照標準,__proto__是不對外公開的,也就是說是個私有屬性,但是Firefox的引擎將他暴露了出來成為了一個共有的屬性,我們可以對外訪問和設置。

好,概念說清了,讓我們看一下下面這些代碼:

<script type="text/javascript">
    var Person = function () { };
        Person.prototype.Say = function () {
        alert("Person say");
    }
    var p = new Person();
    p.Say();
</script>

這段代碼很簡單,相信每個人都這樣寫過,那就讓我們看下為什么p可以訪問Person的Say。

首先var p=new Person();可以得出p.__proto__=Person.prototype。那么當我們調用p.Say()時,首先p中沒有Say這個屬性, 于是,他就需要到他的__proto__中去找,也就是Person.prototype,而我們在上面定義了 Person.prototype.Say=function(){}; 于是,就找到了這個方法。

好,接下來,讓我們看個更復雜的。

<script type="text/javascript">
    var Person = function () { };
        Person.prototype.Say = function () {
        alert("Person say");
    }
    Person.prototype.Salary = 50000;
 
    var Programmer = function () { };
    Programmer.prototype = new Person();
    Programmer.prototype.WriteCode = function () {
        alert("programmer writes code");
    };
 
    Programmer.prototype.Salary = 500;
 
    var p = new Programmer();
    p.Say();
    p.WriteCode();
    alert(p.Salary);
</script>

我們來做這樣的推導:

var p=new Programmer()可以得出p.__proto__=Programmer.prototype;

而在上面我們指定了Programmer.prototype=new Person();我們來這樣拆分,var p1=new Person();Programmer.prototype=p1;那么:

p1.__proto__=Person.prototype;

Programmer.prototype.__proto__=Person.prototype;

由根據上面得到p.__proto__=Programmer.prototype。可以得到p.__proto__.__proto__=Person.prototype。

好,算清楚了之后我們來看上面的結果,p.Say()。由于p沒有Say這個屬性,于是去p.__proto__,也就是 Programmer.prototype,也就是p1中去找,由于p1中也沒有Say,那就去p.__proto__.__proto__,也就是 Person.prototype中去找,于是就找到了alert(“Person say”)的方法。

其余的也都是同樣的道理。

這也就是原型鏈的實現原理。

最后,其實prototype只是一個假象,他在實現原型鏈中只是起到了一個輔助作用,換句話說,他只是在new的時候有著一定的價值,而原型鏈的本質,其實在于__proto__

如果還不明白,可以看以下解析:

__proto__、prototype傻傻分不清楚? 記住以下兩點:
1.__proto__是每個對象都有的一個屬性,而prototype是函數才會有的屬性。
2.__proto__指向的是當前對象的原型對象,而prototype指向的,是以當前函數作為構造函數構造出來的對象的原型對象。看起來有點繞,我 show you the code:

//在JavaScript的世界中,所有的函數都能作為構造函數,構造出一個對象
   //下面我給自己構造一個女神做對象
   function NvShen () {
     this.name = "Alice";
   }
   //現在我設置NvShen這個函數的prototype屬性
   //一般來說直接用匿名的對象就行,我這里是為了方便理解,
   //先定義一個hand對象再把hand賦值給NvShen的prototype
   var hand = {
     whichOne: "right hand",
     someFunction: function(){
       console.log("not safe for work.");
     }
   };
   NvShen.prototype = hand; 

   //這個時候,我們可以用NvShen作為構造函數,構造出myObject對象
   var myObject = new NvShen();
   console.log(myObject.__proto__ === NvShen.prototype) //true

好了,通過上面的代碼,我們構建了一個女神對象myObject,而myObject的原型是hand對象,而剛好myObject的構造函數NvShen()的prototype屬性也指向hand對象。現在我們知道,prototype與__proto__的關系就是:你的__proto__來自你構造函數的prototype

還有,上面的例子中,myObject是通過new NvShen()創建的,而hand對象,則是賦值語句創建的,這有什么不同?

其實hand這種直接用賦值語句加花括號來創建的對象,叫做對象字面量,你可以想象JavaScript內置了一個叫Object()的構造函數,這個函數的prototype屬性指向的是一個空對象:

console.log(Object.prototype) //輸出{}

而所有對象字面量都是通過Object()構造出來的,換言之,對象字面量的__proto__屬性都指向Object.prototype, which is 一個空對象。

所以我們可以知道, hand.__proto__指向的是Object.prototype 再附送你一個fun fact:
Object.prototype這個對象,它的__proto__指向的是null,然后就沒有然后了。

最后如果你還是不太明白,可以看各位知乎大神的回答:js中proto和prototype的區別和關系?

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內容