變量
1.JavaScript hoisting
>>請看例子,我們拿Chrome的console作為JS的運行環境。
上面直接執行console.log(a), 不帶一點懸念地拋出了not defined 錯誤。這是預料之中的。
看下面進化后的代碼:
之前變量沒有定義的錯誤沒了,取而代之的是告訴我們a的值是 'undefined'。先不管a的值緣何為 'undefined' 了,至少可以知道現a這個變量是定義了,因為之前報的' a is not defined'的錯誤沒有了。
這正是因為JavaScript 中的一個聲明提前的特性起的作用。
JavaScript中可以提前使用在后面語句中聲明的變量,這種特性叫被國外某網友(ben cherry)稱為Hoisting (非官方術語) 。 可以理解為將變量的聲明提前了,所以在變量聲明前使用變量不會報錯。而且這一特性不僅限于變量名,對于函數的聲明也是同樣的效果。
第一次對函數foo()的調用同樣報 'not defined' 錯誤。這是合情合理同時是合法的。因為從頭到尾就沒有定義這么一個叫作foo() 的東西。
之后將函數調用寫在最前面,但函數的定義我們寫在了之后。
>> 再來看上面對a的輸出值為'undefined'的問題。
這里需要深入理解Hoisting這一特性。它的提前只是將聲明提前,而對變量的賦值并沒有跟著提前。這點很關鍵。也就是為什么我們可以在第一句使用變量a但它的值卻是 'undefined'。 JavaScript里面聲明了但還未賦值的變量其值默認便是 'undefined'。
按照Hoisting來解釋,最終生成的等價代碼其實差不多應該就是這樣的:
2.直接對字符串字面量調用其方法
可以直接對字符串字面量調用方法,JS解析器會自動把字符串變量轉為一個字符串類型暫存,然后調用字符串上的方法,完了之后再將暫存的字符串類型銷毀。
所以你會看到下面這種用法。
我們可以理解為解析器在后臺聲明了一個變量暫存這個字符串然后再調用的length方法。
函數
1.匿名函數無法在聲明前調用
正如上面介紹的Hoisting特性,函數可以定義在調用之前也可以定義在調用之后:
但僅限于上述這種方式定義的函數。對于匿名函數上述規則不適用。
2. 參數變更影響到函數外部
當傳遞給函數的參數是一個數組或對象時,在函數體內對這個傳入的參數的更改會影響到函數體外的原傳入值。
一般說來,對參數的更改不會影響到原來變量的值,更改只在函數體內起作用:
上述代碼中,將name變量賦值為'Wayou Liu' 然后傳入change() 函數。在函數體內將傳入的參數值改為'Liu Wayou'。然后再輸出,不出意外地,輸出的是變量原來的值'Wayou Liu'。因為當name傳入change函數后,在函數體內,相當于有一個name的副本,這個副本的值等于name,之后在函數體內對其做的操作是在這個副本上進行的。
但情況有所不同,當傳入的參數是數組、對象時,在函數體內對參數所做的更改會反映到原變量上。
可以看出,上面代碼中已經把friut數組的第一個元素更改了。
下面是關于對象的例子:
可以很明顯地看到函數體內對參數的改動影響到了原來的變量,這與通常情況下的傳參有質的區別了。需要特別注意。
But,當在函數體內對傳入的數組或對象賦值時,這個更改不會反映到函數體外的原變量身上!
請看:
按照上面函數內部的更改會反映到原變量的理論,你肯定覺得執行完change()后person變量的name屬性的值已經變成'Tom'了吧。但結果讓人有點難以接受。
原因在于,當在函數體內使用賦值操作時,系統就創建了一個變量名為p的變量。這個p是函數內部的變量,對它進行賦值當然只在函數體內起作用,外面的person還是原來的person。
這一步與原來代碼的操作差別僅在于在函數體內是對參數賦新值呢還是對參數的屬性或數組的元素進行更改。
3.使用arguments來接收個數未定的參數
有時候我們寫了個函數但它接收的參數個數不確定,有時候我們在調用一個函數的時候或許也不確定要傳多少個參數。參數需要變成動態的。
這時可以通過函數內arguments來接收傳遞到一個函數的所有參數。
具體說來,就是在函數體內默認有個arguments變量,它保存了調用這個函數時傳遞來的所有參數,總個數及每個參數的值。它是一個類似于數組的變量,每個參數會成為它的一個元素,可以通過游標來進行訪問。
通過arguments.length可以知道參數個數。
下面這個例子來自MDN:
文章列表