請教變數作用域問題,下面代碼彈出的為什麼不是global?

var a = "global";

function foo() {

alert(a);

var a = "local";

}

foo();

為什麼彈出框為undefined?

不應當在函數體查找不到該變數後沿著作用域鏈在全局對象中找到變數a = "global"么?


cminor的解釋並不準確。

這個問題可以用執行上下文(Execution Context)的執行規則解釋。執行上下文分為global object、function、eval三種類型。在運行某個執行上下文時,會分為兩個步驟執行:

  1. 進入該執行上下文。這個階段會將代碼中所有的形參、變數聲明和函數聲明作為變數對象(Variable Object,在function execution context中為Active Object)的屬性保存。變數對象是執行上下文對象的一個屬性。
  2. 執行代碼。這個階段解析器開始逐行解析代碼。這個過程中需要用到執行上下文的另外一個重要屬性作用域鏈(Scope chain),它的作用是用來找到所有變數、函數,以便進行相關操作。作用域鏈實際就是一串變數對象指針,頂部總是保存作用域鏈所在執行上下文的變數對象的指針。這也正是局部變數覆蓋上級變數的原因。

具體請見:http://dmitrysoshnikov.com/ecmascript/chapter-2-variable-object/#phases-of-processing-the-context-code


瀏覽器在解析 js 的時候,會有一個預處理,比如把一個作用域下的所有變數聲明都移到作用域開始的位置,所以這段代碼執行的時候就相當於:

var a = "global";

function foo() {

var a;

alert(a);

a = "local";

}

foo();

就是 undefined 了~

所以一般比較推薦將一個函數中所有的變數聲明都放到函數最前,不容易出一些小問題。

【注】

這個回答只是一個「看上去是這樣」的解釋,並不是一個十分嚴謹的解釋,可以參考 @尚春 的回答。

犀牛書 6th edition 在 3.10.1《Function Scope and Hoisting》(其他版本的章節號可能不同)也提到了這個問題,但並沒有詳細解釋,只是說了 js 是 function scope 而不像 C 之類的語言是 block scope。

如果想詳細地了解一下這個問題的根本,可以閱讀 ECMA-262 5.1 edition 標準的第 10 章《Executable Code and Execution Contexts》(其他版本的章節號可能不同),雖然我還沒耐著性子把它讀完過。。。。

另外,關於 scope chain 這個玩意,High Performance JavaScript 第二章《Data Access》里說了一些,也是可以看看的。


var function_name = function() {}和function function_name() {}的區別就在於此,後者在執行前會被提到最前進行預處理,所以裡面的變數看似是全局變數,其實當時是沒有的,就會出現@cminor提到的樣子很像的這樣:

function foo() {

alert(a);

a = "local";

}

var a = "global";

foo();

這個時候因為foo內部有變數a的賦值語句,所以會補上var a;的聲明在最前面,也就是

function foo() {

var a;

alert(a);

a = "local";

}

於是就是undefined了

所以我個人習慣是函數聲明統一用第一種形式


var a = "global";

function foo() {

alert(a);

//var a = "local";//把這句話注釋掉看看。

}

foo();

簡單直觀地理解,同意 cminor 的答案。

更深入看這裡:http://www.cnblogs.com/justinw/archive/2010/04/23/1718733.html


推薦閱讀:

求解一小段代碼,完全不理解eval的作用機制?
有關JS中作用域的問題,全局作用域中的變數不應該在全局變數對象中嗎,為什麼下面的函數搜索不到全局變數?
為什麼ECMAScript不原生支持重載?
這種所有圖片一直佔據100%寬度的響應式是如何做的?

TAG:前端開發 | JavaScript |