關於Dmitry Baranovskiy 的博客中一篇文章(http://dmitry.baranovskiy.com/post/91403200),其中有五段小代碼,用來測試是否理解 JavaScript 的核心,閉包和作用域, 該文章也在csdn論壇上受到過關注和討論, 集思廣益,下面結合自己的理解,做了如下小結。
1
if (!("a" in window)) { var a = 1; } console.log(a);
程序會首先解析所有聲明的函數,其次是var聲明的變量,因為javascript沒有塊的概念,所以if(){...}中,var聲明了 a = 1, a是依然屬於全局變量。
執行等價於:
var a; //全局 if (!("a" in window)) { a = 1; } console.log (a);
(1)開始時,聲明了變量a,但並沒有賦值,所以a = undefined , 而undefined 存在於window中,所以(’a’ in window)返回true, 取反為false, 這樣就不會執行大括號里面的 “a=1” 的語句。
(2) console.log(a); //undefined
2
var a = 1, b = function a (x) { x && a (--x); }; console.log (a);
可以用一個var,來聲明多個變量,中間用多個逗號分開,執行等價於:
var a = 1; var b = function a(x){ x && a(--x); } console.log(a);
(1) 函數表達式類似於局部變量,不會被全局作用域訪問到,所以這里的函數 function a 是局部變量,外部無法訪問,因此全局a還是1;
(2) console.log(a); //1
3
function a (x) { return x * 2; } var a; console.log(a);
javascript永遠是先解析聲明函數,再解析變量, 執行順序如下:
(1) 解析函數a;
(2) 聲明變量var a; 因為a此時並沒有被賦值,所以它為 undefined, 還是指向原來的值,即函數 function a;
(3) console.log(a); // function a
4
function b (x, y, a) { arguments[2] = 10; console.log (a); } b(1, 2, 3);
函數內部,可以引用一個類數組的對象arguments,它並不是真正的數組,代表了函數實際接受參數的集合,可以通過下標對相應參數進行訪問,
如果修改了此對象的屬性,如arguments[index],則被傳進來的第index (如果有的話,下標從0開始) 變量的值也會被修改。
執行順序:
(1) 聲明一個函數b;
(2) 執行函數b(1,2,3);因為這里arguments[2]與變量a引用的是同一個值,所以當arguments[2]改變時,a也隨之改變。
(3) console.log(a) // 10;
5
function a () { console.log(this); } a.call (null);
call 調用一個對象的一個方法,以另一個對象替換當前對象。
格式如 call(thisObj, arg1,arg2...argN);
在函數體外部調用call()方法,如果傳入null,則默認轉成window,如果不傳也是一樣,即函數中的this指向window。
console.log(this) // window;
function a () { console.log (this === window); } console.log(this === window); // true a.call (); // true a.call (null); // true a.call (this); // true a.call (window); // true a(); // true
6
function fo(){ console.log(a); } function foo(){ var a = 2; fo(); } foo();
先執行 foo 函數, fo 雖然在foo調用,但是 fo函數是聲明在全局作用域下的,所以fo中引用的a,是指向全局的window,而全局作用域下的a 並未聲明,雖然在 foo 下,聲明了var a=2,但它作為局部變量,無法被函數外的作用域所調用。
console.log(a) // a is not defined;
如果將以上代碼寫成:
function foo(){ var a = 2; function fo(){ console.log(a); } fo(); } foo();
因為這時候,函數fo是聲明在foo函數體內的,屬於foo的內部函數,作用域鏈的訪問順序是由內向外的,a在fo里搜索不到,就會到上一級函數foo中 尋找,這里找到var a = 2 后返回結果。
console.log(a) // 2;
