關(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ù)清除趾代。
總結(jié)一下就是,如果Heap中一個對象的數(shù)據(jù)沒有Stack的地址引用丰辣,那么數(shù)據(jù)清除撒强。
原型和原型鏈的意義(這里以chrome瀏覽器的控制臺為例)
現(xiàn)在假設(shè)我們在數(shù)據(jù)中不生成原型和原型鏈,那么每當(dāng)生成對象的時候笙什,Heap中對象數(shù)據(jù)里面的哈希表就是
//以聲明數(shù)值的函數(shù)舉例
new Number(1)
注意這并不是所有的飘哨,有很多我并沒有展開
也就是說每次創(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ū)域的火本。
既然是儲存在兩個區(qū)域的,就必須有一個引用聪建,鏈接兩個不同的區(qū)域钙畔,完成這個功能的就是
.__proto__
然后下面是剛才介紹的這些原型的結(jié)構(gòu)圖
Number.prototype
:數(shù)值原型String.prototype
:字符串原型Boolean.prototype
:布爾原型Object.prototype
:對象原型可以看出,Object.prototype是所有對象的公有屬性金麸,也就是原型擎析,之上就沒有了(null)。
以其中字符串對象為例挥下,首先原型鏈開端為某字符串對象的自有屬性揍魂,然后其中.__proto__
鏈接String.prototype
,String.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
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í)。