一道小題引發的深思
今天無意中看到一個js筆試題,不由得想起初學js那會被各種題目狂虐的心酸,雖說現在也會被筆試題所虐,但畢竟比之前好了很多,下面就是我的個人理解,歡迎拍磚、指正:
var x = 1; function printx(){ console.log(x); } function show(f){ var x = 2; (function(){ f(); })() } show(printx); //1
結果后台會打印1,而不是2。這有些不合常理,因為很多人會錯誤的認為:函數show中的f()在執行時,由於本作用域中沒有x,所以會向上層作用域尋找x,當找到上層函數show的作用域中時發現 var x=2,這時就把x確定為2,否則會繼續向上找,直至window。。。終於用到所謂的“作用域鏈”啦!大喜,其實這樣想你就錯了!
第一:首先有這么一個名詞叫“自由變量”,自由變量是指:如果作用域(函數)A中使用到了變量x,而x並不是在作用域(函數)A中定義,那么對於作用域(函數)A來說,x就是A的自由變量。
第二:理解這么一句話:js沒有塊級作用域,僅有的塊級作用域是用函數來實現的,也就是說只有函數能創建出一塊獨立的作用域。if、for代碼塊都不行!所以本文中一個作用域可以理解為一個函數。
第三:函數的作用域在函數定義時就已經確定,而不是在執行時確定。
對於嵌套函數來說按照上述:尋找自由變量是沿作用域鏈向上一層層查找,這樣理解是對的。但是這樣理解太片面,甚至會產生錯誤,就比如上面這道題。正確的理解應該是:尋找自由變量時會到創建這個函數(作用域)的那個作用域中尋找。而創建這個函數的作用域並不一定是它位置上的父級作用域,(並不是在代碼結構上包含,就是子父關系)上面這道題就是這種情況:函數show中把printx函數作為參數傳入執行,在執行時會尋找創建它的那個作用域,很明顯創建printx函數的作用域並不是show,而是全局window(因為printx函數在window中創建),所以它會尋找全局window中的x,此x為1。 所以會打印1,這才是正解。
不知道我解釋的清不清楚,總之:函數使用自由變量時,會到創建這個函數的那個作用域中尋找。“向父級作用域中尋找”可能會存在偏差。(不足之處歡迎指正!)
再補充兩道,加深理解
本文的評論里還有兩道更加通俗易懂的兩道題,征得熱心博友theWalker的同意,把他給出的兩個demo也展示給大家,希望對你有所幫助:
function a(){ console.log(b) } function c(){ var b = 1; a() } c() //b is not defined /****************************************/ var b = 2; function a(){ console.log(b) } function c(){ var b = 1; a() } c() // 2