關鍵詞: 原型(prototype)和原型鏈(__proto__) 數據結構 全局對象

本文的主要脈絡:介紹為什麼會有原型鏈和原型,另外涉及一部分的全局對象,然後從數據結構的角度來理清他們的結構。在本文的最後會附錄上收集到的一些全局變數的API。

載入超時,點擊重試

原型和原型鏈的由來

垃圾回收問題

我們在說明這個事情之前先說明一個問題。那就是當我們在瀏覽器裡面使用JavaScript,我們通常會聲明許許多多的數據類型,那麼在不斷地累積過程,必然會有一個回收機制,把不需要的數據清除,留出內存。那麼瀏覽器是如何知道某個數據是否還需要呢?在Stack(保存原始/基本數據類型和對象地址的區域)中如果我們聲明瞭那麼就一直存在,除非我們清除或者關閉瀏覽器,那麼在Heap(保存對象地址引用的數據的區域)中,如果被引用的數據不再有地址能引用到,那麼數據清除。

總結一下就是,如果Heap中一個對象的數據沒有Stack的地址引用,那麼數據清除。

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

現在假設我們在數據中不生成原型和原型鏈,那麼每當生成對象的時候,Heap中對象數據裡面的哈希表就是

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

注意這並不是所有的,有很多我並沒有展開 也就是說每次創建一個數值、字元串、布爾或者普通對象,就有一大推的哈希表跟在後面,這樣既重複,又浪費空間的事是不被看好的,所以把他們公有的屬性提煉出來,單獨封裝好,每次要使用的時候引用就可以了,這就是原型(prototype)。

原型有哪些?(只說現在學到的幾個)

Number.prototype:數值原型 String.prototype:字元串原型 Boolean.prototype:布爾原型 Object.prototype:對象原型 一些常見的API附在本文最後

那麼什麼是原型鏈呢?

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

圖片中說得非常清楚了,這個就是一個哈希表的key,而他的value指向一個地址,那就是Number.prototype這個原型,而在這個__proto__:Number目錄下的所有的哈希表都是Number.prototype裡面的數據。這裡雖然他們寫在一起,但是實際上在Heap中他們儲存的區域並不是同一個 證明一下

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

這個代碼是在控制檯運行的,我們聲明a和b對象,讓他們內容完全相同,但是a === b返回的是false,這說明瞭他們儲存位置不一樣。然後調用.__proto__的屬性,發現a.__proto__ === b.__proto__是相同的,那就是因為他們調用的數值原型是同一個(調用的原型儲存位置相同)!所以.__proto__調用的屬性Number.prototype和對象自有的屬性是儲存在兩個區域的。

既然是儲存在兩個區域的,就必須有一個引用,鏈接兩個不同的區域,完成這個功能的就是.__proto__ 然後下面是剛才介紹的這些原型的結構圖 Number.prototype:數值原型 String.prototype:字元串原型 Boolean.prototype:布爾原型 Object.prototype:對象原型

可以看出,Object.prototype是所有對象的公有屬性,也就是原型,之上就沒有了(null)。 以其中字元串對象為例,首先原型鏈開端為某字元串對象的自有屬性,然後其中.__proto__鏈接String.prototypeString.prototype.__proto__鏈接Object.prototype,最後指向null,原型鏈結束。 由一個末尾子節點開始一直到null結束,整個過程就是原型鏈

有一個問題

既然我們的原型是被引用的,原型本身也是對象,並且要早於我們用代碼生成某個對象的時間點之前,就生成原型在瀏覽器(Heap)中。那麼他將如何在沒有被引用的情況下不被垃圾回收機制回收呢?(如果Heap中一個對象的數據沒有Stack的地址引用,那麼數據清除。) 答案是:全局對象來引用,在瀏覽器打開時,這些原型就被瀏覽器的全局對象引用生成。這些全局對象(部分)有

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

題外話

當看到null為原型鏈末端時,而原型鏈上每個節點都是對象,讓我想起來了一個命令

typeof null
"object"

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


附:

Number.prototype

  • Number() 函數把對象的值轉換為數字。 -toExponential()方法以指數表示法返回該數值字元串表示形式。 alert("numObj.toExponential() is " + numObj.toExponential()); //輸出 7.71234e+1
  • toFixed()方法使用定點表示法來格式化一個數。 numObj.toFixed(); // 返回 "12346":進行四捨五入,不包括小數部分
  • toLocaleString()方法返回這個數字在特定語言環境下的表示字元串。
  • toPrecision() 方法以指定的精度返回該數值對象的字元串表示。 ```var numObj = 5.123456; 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`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number "JavaScript 的 Number 對象是經過封裝的能讓你處理數字值的對象。Number 對象由 Number() 構造器創建。") 對象的字元串表示形式。
- **`valueOf()`** 方法返回一個被 [`Number`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number "JavaScript 的 Number 對象是經過封裝的能讓你處理數字值的對象。Number 對象由 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()構造函數創建一個對象包裝器。 hasOwnProperty()所有繼承了 Object 的對象都會繼承到 hasOwnProperty 方法。這個方法可以用來檢測一個對象是否含有特定的自身屬性,該方法會忽略掉那些從原型鏈上繼承到的屬性。 isPrototypeOf()方法用於測試一個對象是否存在於另一個對象的原型鏈上。 propertyIsEnumerable()每個對象都有一個propertyIsEnumerable方法。此方法可以確定對象中指定的屬性是否可以被枚舉,但是通過原型鏈繼承的屬性除外。如果對象沒有指定的屬性,則此方法返回falsetoLocaleString() toString() valueOf() Object.prototype

本文是作者學習所獲,歡迎大家指正錯誤,共同學習。

推薦閱讀:

查看原文 >>
相關文章