原型和原型鏈的由來&在數(shù)據(jù)結(jié)構(gòu)中的關(guān)系

關(guān)鍵詞: 原型(prototype)和原型鏈(__proto__) 數(shù)據(jù)結(jié)構(gòu) 全局對象

本文的主要脈絡(luò):介紹為什么會有原型鏈和原型糟需,另外涉及一部分的全局對象止潘,然后從數(shù)據(jù)結(jié)構(gòu)的角度來理清他們的結(jié)構(gòu)犬金。在本文的最后會附錄上收集到的一些全局變量的API。


原型和原型鏈的由來

垃圾回收問題

我們在說明這個事情之前先說明一個問題吼拥。那就是當(dāng)我們在瀏覽器里面使用JavaScript溪掀,我們通常會聲明許許多多的數(shù)據(jù)類型光稼,那么在不斷地累積過程齿拂,必然會有一個回收機制擎椰,把不需要的數(shù)據(jù)清除,留出內(nèi)存创肥。那么瀏覽器是如何知道某個數(shù)據(jù)是否還需要呢?在Stack(保存原始/基本數(shù)據(jù)類型和對象地址的區(qū)域)中如果我們聲明了那么就一直存在值朋,除非我們清除或者關(guān)閉瀏覽器叹侄,那么在Heap(保存對象地址引用的數(shù)據(jù)的區(qū)域)中,如果被引用的數(shù)據(jù)不再有地址能引用到昨登,那么數(shù)據(jù)清除趾代。


JavaScript保存數(shù)據(jù)方式.png

總結(jié)一下就是,如果Heap中一個對象的數(shù)據(jù)沒有Stack的地址引用丰辣,那么數(shù)據(jù)清除撒强。

原型和原型鏈的意義(這里以chrome瀏覽器的控制臺為例)

現(xiàn)在假設(shè)我們在數(shù)據(jù)中不生成原型和原型鏈,那么每當(dāng)生成對象的時候笙什,Heap中對象數(shù)據(jù)里面的哈希表就是

//以聲明數(shù)值的函數(shù)舉例
new Number(1)

部分的哈希表.png

注意這并不是所有的飘哨,有很多我并沒有展開
也就是說每次創(chuàng)建一個數(shù)值、字符串琐凭、布爾或者普通對象芽隆,就有一大推的哈希表跟在后面,這樣既重復(fù)统屈,又浪費空間的事是不被看好的胚吁,所以把他們公有的屬性提煉出來,單獨封裝好愁憔,每次要使用的時候引用就可以了腕扶,這就是原型(prototype)。

原型有哪些吨掌?(只說現(xiàn)在學(xué)到的幾個)

Number.prototype:數(shù)值原型
String.prototype:字符串原型
Boolean.prototype:布爾原型
Object.prototype:對象原型
一些常見的API附在本文最后

那么什么是原型鏈呢半抱?

說清楚這個之前我們必須先清楚__proto__是什么?


圖片中說得非常清楚了膜宋,這個就是一個哈希表的key代虾,而他的value指向一個地址,那就是Number.prototype這個原型激蹲,而在這個__proto__:Number目錄下的所有的哈希表都是Number.prototype里面的數(shù)據(jù)棉磨。這里雖然他們寫在一起,但是實際上在Heap中他們儲存的區(qū)域并不是同一個
證明一下

var a = new Number(1)
var b = new Number(1)
a === b
false
a.__proto__ === b.__proto__
true

這個代碼是在控制臺運行的学辱,我們聲明a和b對象乘瓤,讓他們內(nèi)容完全相同环形,但是a === b返回的是false,這說明了他們儲存位置不一樣衙傀。然后調(diào)用.__proto__的屬性抬吟,發(fā)現(xiàn)a.__proto__ === b.__proto__是相同的,那就是因為他們調(diào)用的數(shù)值原型是同一個(調(diào)用的原型儲存位置相同)统抬!所以.__proto__調(diào)用的屬性Number.prototype和對象自有的屬性是儲存在兩個區(qū)域的火本。

例子中的數(shù)據(jù)示意.png

既然是儲存在兩個區(qū)域的,就必須有一個引用聪建,鏈接兩個不同的區(qū)域钙畔,完成這個功能的就是.__proto__
然后下面是剛才介紹的這些原型的結(jié)構(gòu)圖
Number.prototype:數(shù)值原型
String.prototype:字符串原型
Boolean.prototype:布爾原型
Object.prototype:對象原型
原型鏈.png

可以看出,Object.prototype是所有對象的公有屬性金麸,也就是原型擎析,之上就沒有了(null)。
以其中字符串對象為例挥下,首先原型鏈開端為某字符串對象的自有屬性揍魂,然后其中.__proto__鏈接String.prototypeString.prototype.__proto__鏈接Object.prototype棚瘟,最后指向null现斋,原型鏈結(jié)束。
由一個末尾子節(jié)點開始一直到null結(jié)束偎蘸,整個過程就是原型鏈

有一個問題

既然我們的原型是被引用的步责,原型本身也是對象,并且要早于我們用代碼生成某個對象的時間點之前禀苦,就生成原型在瀏覽器(Heap)中蔓肯。那么他將如何在沒有被引用的情況下不被垃圾回收機制回收呢?(如果Heap中一個對象的數(shù)據(jù)沒有Stack的地址引用振乏,那么數(shù)據(jù)清除蔗包。
答案是:全局對象來引用,在瀏覽器打開時慧邮,這些原型就被瀏覽器的全局對象引用生成调限。這些全局對象(部分)有

Number()
String()
Boolean()
Object()

題外話

當(dāng)看到null為原型鏈末端時,而原型鏈上每個節(jié)點都是對象误澳,讓我想起來了一個命令

typeof null
"object"

雖然這個bug和這個只是巧合耻矮,但是確實有趣。


附:

Number.prototype

  • Number() 函數(shù)把對象的值轉(zhuǎn)換為數(shù)字忆谓。
    -toExponential()方法以指數(shù)表示法返回該數(shù)值字符串表示形式裆装。
    alert("numObj.toExponential() is " + numObj.toExponential()); //輸出 7.71234e+1
  • toFixed()方法使用定點表示法來格式化一個數(shù)。
    numObj.toFixed(); // 返回 "12346":進行四舍五入,不包括小數(shù)部分
  • toLocaleString()方法返回這個數(shù)字在特定語言環(huán)境下的表示字符串哨免。
  • toPrecision() 方法以指定的精度返回該數(shù)值對象的字符串表示茎活。
console.log("numObj.toPrecision()  is " + numObj.toPrecision());  //輸出 5.123456
console.log("numObj.toPrecision(5) is " + numObj.toPrecision(5)); //輸出 5.1235
console.log("numObj.toPrecision(2) is " + numObj.toPrecision(2)); //輸出 5.1
console.log("numObj.toPrecision(1) is " + numObj.toPrecision(1)); //輸出 5
  • toString() 方法返回指定 Number 對象的字符串表示形式。
  • valueOf() 方法返回一個被 Number 對象包裝的原始值琢唾。
var numObj = new Number(10);
console.log(typeof numObj); // object

var num = numObj.valueOf();
console.log(num);           // 10
console.log(typeof num);    // number

String.prototype
太多了载荔,不一一寫

Boolean.prototype
new Boolean([value])Boolean對象是一個布爾值的對象包裝器。
toString() 方法返回指定的布爾對象的字符串形式采桃。
bool.valueOf()返回Boolean的原始值

Object.prototype
Object()構(gòu)造函數(shù)創(chuàng)建一個對象包裝器懒熙。
hasOwnProperty()所有繼承了 Object 的對象都會繼承到 hasOwnProperty 方法。這個方法可以用來檢測一個對象是否含有特定的自身屬性普办,該方法會忽略掉那些從原型鏈上繼承到的屬性工扎。
isPrototypeOf()方法用于測試一個對象是否存在于另一個對象的原型鏈上。
propertyIsEnumerable()每個對象都有一個propertyIsEnumerable方法泌豆。此方法可以確定對象中指定的屬性是否可以被枚舉,但是通過原型鏈繼承的屬性除外吏饿。如果對象沒有指定的屬性踪危,則此方法返回false
toLocaleString()
toString()
valueOf()
Object.prototype

本文是作者學(xué)習(xí)所獲猪落,歡迎大家指正錯誤贞远,共同學(xué)習(xí)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末笨忌,一起剝皮案震驚了整個濱河市蓝仲,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌官疲,老刑警劉巖袱结,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異途凫,居然都是意外死亡垢夹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門维费,熙熙樓的掌柜王于貴愁眉苦臉地迎上來果元,“玉大人,你說我怎么就攤上這事犀盟《梗” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵阅畴,是天一觀的道長倡怎。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么诈胜? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任豹障,我火速辦了婚禮,結(jié)果婚禮上焦匈,老公的妹妹穿的比我還像新娘血公。我一直安慰自己,他們只是感情好缓熟,可當(dāng)我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布累魔。 她就那樣靜靜地躺著,像睡著了一般够滑。 火紅的嫁衣襯著肌膚如雪垦写。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天彰触,我揣著相機與錄音梯投,去河邊找鬼。 笑死况毅,一個胖子當(dāng)著我的面吹牛分蓖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播尔许,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼么鹤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了味廊?” 一聲冷哼從身側(cè)響起蒸甜,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎余佛,沒想到半個月后柠新,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡辉巡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年登颓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片红氯。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡框咙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出痢甘,到底是詐尸還是另有隱情喇嘱,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布塞栅,位于F島的核電站者铜,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜作烟,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一愉粤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拿撩,春花似錦衣厘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至探赫,卻和暖如春型宙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伦吠。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工妆兑, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人毛仪。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓搁嗓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親潭千。 傳聞我的和親對象是個殘疾皇子谱姓,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容