準備寫點亂七八糟的文章,對JavaScript的胡扯
賦詩一首:
物件本無根,
型別亦無形。
本來無一物,
何處惹塵埃?
一、JavaScript的記憶體模型
JavaScript的本質是一個物件,一個物件可以包含多個屬性,物件的屬性可以分為直接量和物件兩種型別,而物件又分為Object物件和function物件兩種型別。
按照資料型別劃分:
- 簡單資料型別
- number
- string
- boolean
- 特殊資料型別
- null
- undefined
- 複雜資料型別
- object
- Object物件
- function物件
- object
直接量和物件的兩種型別的屬性在記憶體中儲存方式不同(跟Java中的類似)
-
直接量:直接用兩塊記憶體分別儲存屬性名和屬性值
-
物件:需要三塊記憶體,分別儲存屬性名、屬性地址和屬性內容
對於物件型別的屬性來說,屬性名知識指向了儲存物件的記憶體地址,而不是執行實際的物件,程式碼演示如下:
function F(){
this.v = 1;
}
var f1 = new F();
var f2 = f1;
console.log(f2.v);
f2.v = 2;
console.log(f1.v);
f1 = null;
console.log(f2.v);
複製程式碼
程式碼的執行圖解:
我們一直都說JavaScript是指令碼語言,在瀏覽器中解釋執行的,不應該有自己的記憶體模型,其實不是這樣的。無論編譯語言還是解釋型語言,他們的變數、函式、物件等資料都是儲存在記憶體當中的,使用時需要通過變數名在指定地方找到對應的具體內容,然後再進行實際操作。
二、在JS中函式是如何執行的
函式我們之前已經都接觸過了,函式無非有兩部分:資料和對資料的操作。資料有分為外部資料和內部資料。對於外部資料我們先不進行說明,這裡主要是說函式,內部資料有分為引數和變數兩個部分。
引數(形參):在函式每次執行的時候引數都會被賦予一個新的數值;
變數(區域性變數):每次都會設定為一個相同的初始值;
-
函式的變數和引數是如何儲存的呢?
函式在每次執行之前都會新建一個引數陣列和一個變數陣列(當然也可以合併為一個陣列,而通常會使用棧來實現),然後將呼叫時所傳遞的引數設定到引數陣列中,而變數陣列在每次執行都具有相同的內容。簡單的資料會直接儲存在陣列當中,而複雜的資料,陣列只是儲存地址,具體的資料儲存在堆中。
function paramTest(p1){ var message = "Hello World"; console.log(p1); for(var i in arguments){ console.log(arguments[i]); } } //函式的呼叫 paramTest("a","b","c"); //輸出結果為: a a b c 複製程式碼
我們使用了Chrome的除錯,函式在執行時會將引數p1和函式中所用到的變數messag、i方法相同的地位,即在函式內部執行的時候不會區分是引數還是變數。在JS的函式中,會自動建立一個名字為
arguments
的內部變數,然後將所有的引數的地址儲存到其中。arguments 類似陣列物件,可以通過它來獲取函式呼叫時所傳遞的引數。paramTest方法首先列印了p1的值,然後遍歷列印arguments中所有的引數的值,可以看出引數p1的值和arguments[0]的值是一樣的,函式的引數按照順序依次儲存在arguments變數中,在呼叫函式時傳入引數的個數也可以和定義時不一樣,所以說JS中不存在同名函式的過載方法。
-
在函式定義的變數時函式級作用域而不是塊級作用域
function scopeTest(){ if(true){ var message = "Hello World"; } console.log(message); } //函式的呼叫 scopeTest(); //輸出結果為:Hello World 複製程式碼
這裡的message是在if語句塊中定義的,但是在if語句外部依然可以進行呼叫。
在JS中的方法執行時會將其自身所有使用var定義的變數統一放到前面介紹的變數陣列當中,所以在一個函式中,所有使用var定義的變數都是同等地位的