Effective JavaScript Item 12 理解Variable Hoisting,variablehoisting
本系列作為Effective JavaScript的讀書筆記。
JavaScript中並沒有Block Scoping,只有Function Scoping。
因此如果在一個Block中定義了一個變數,那麼這個變數相當於是被定義到了這個Block屬於的Function中,比如:
function isWinner(player, others) {var highest = 0;for (var i = 0, n = others.length; i < n; i++) {<span style="color:#ff0000;">var player = others[i];</span>if (player.score > highest) {highest = player.score;}}return player.score > highest;}
上面的代碼中,在for迴圈中聲明了一個變數player,因為Variable Hoisting的原因,這個變數實際上被聲明成了下面這個樣子:
function isWinner(player, others) {<span style="color:#ff0000;">var player;</span>var highest = 0;for (var i = 0, n = others.length; i < n; i++) {<span style="color:#ff0000;">player = others[i];</span>if (player.score > highest) {highest = player.score;}}return player.score > highest;}
因此,傳入到function中的player參數被覆蓋掉了。程式不能按預期行為運行。
在JavaScript中,對於變數的聲明實際上包含了兩個部分:
- 聲明本身
- 賦值
JavaScript會通過Variable Hoisting將第一個部分,也就是聲明的部分,放到包含變數的function的頭部。
在下面這個例子中:
function trimSections(header, body, footer) {for (var i = 0, n = header.length; i < n; i++) {header[i] = header[i].trim();}for (var i = 0, n = body.length; i < n; i++) {body[i] = body[i].trim();}for (var i = 0, n = footer.length; i < n; i++) {footer[i] = footer[i].trim();}}
因為VariableHoisting的緣故,i和n都會被放到function的開始處,所以實際上只聲明了兩個變數,在for迴圈中,會對它們進行賦值,實際的行為是這樣的:
function trimSections(header, body, footer) {<span style="color:#ff0000;">var i, n;</span>for (i = 0, n = header.length; i < n; i++) {header[i] = header[i].trim();}for (i = 0, n = body.length; i < n; i++) {body[i] = body[i].trim();}for (i = 0, n = footer.length; i < n; i++) {footer[i] = footer[i].trim();}}
正因為VariableHoisting的存在,一些程式員偏好將變數定義在function的開始處,相當於完成了一次手動地Hoisting。這樣做的目的是為了避免歧義性,讓代碼更加清晰。
但是,有一個特殊情況,在try…catch語句中,catch block中的參數會被綁定到該block中:
function test() {var x = "var", result = [];result.push(x);try {throw "exception";} catch (x) {<span style="color:#ff0000;">x = "catch";</span>}result.push(x);return result;}test(); // ["var", "var"]
重點:
- 在Block中聲明的變數會被隱式地Hoisted到包含它們的function的開始處。(除了上面提到的catch block)
- 在一個function中,多次聲明一個變數最終只會聲明一個變數。
- 為了避免歧義性,考慮將變數聲明在function的開始處。