最近看到一個面試題以下:前端
var x=10; function fn() { console.log(x); } function show(f) { var x=20; (function() { f(); })(); } show(fn); //10
最後的輸出結果是10而不是20,是否是不好異,不是有做用域鏈嗎,不是內部函數有權訪問外部函數的變量嗎,世界怎麼了,以前的理論都崩塌了嗎??囧。。。面試
其實不是的,以前的理論沒錯,只是本身的理解有問題。函數執行時確實是沿着做用域鏈查找變量的,問題是什麼是做用域鏈?紅寶書上的解釋:其實做用域鏈在函數定義時已經肯定了,做用域鏈是和函數定義時的位置相關的。在函數建立的時候建立一個包含外部對象(包括全局對象和全部包含本身的對象)的做用域鏈,儲存在內部[[scope]]屬性中。函數執行的時候會建立一個執行環境,經過複製[[scope]]屬性中的對象,構建執行環境的做用域鏈,並把本身的活動對象推向當前做用域鏈的前端以此造成完整的做用域鏈。[[scope]]屬性中保存的是對可訪問變量對象的引用,而不是值的複製。函數
因此做用域鏈是和函數建立時的位置相關的:spa
var x=10; function fn(){ console.log(x); } function show() { var x=20; fn(); } show(); //輸出10 ----------------------- var x=10; function show() { var x=20; function fn(){// 這裏定義的fn函數,定義時的做用域鏈已經肯定保存在[[Scope]]屬性中,包括show函數中的變量對象和全局變量對象 console.log(x); } fn(); } show(); //輸出20