JS高程 -- chapter( 函式表示式 )
遞迴
var factorial = (function f(n){
if(n<=1){
return 1;
}else{
return n*f(n-1);
}
})
上面這段程式碼使用具名函式表示式來建立遞迴函式,保證 factorial
變數即使指向別的物件也能正確執行遞迴。
( )
中是什麼,它就返回什麼,若是函式,則其命名在全域性是無法訪問的,只有賦值給一個變數才能訪問( 參考立即執行函式 )
閉包
閉包 是指有權訪問另一個函式作用域中變數的函式
作用域鏈細節:
- 函式 建立 的時候,會建立一個執行環境( 執行上下文 ),以及確定作用域鏈,建立 變數 物件
- 函式 執行
arguments
物件和函式內宣告的 變數 的賦值,初始化 活動 物件 - 作用域鏈中,當前函式的 活動 物件 處於鏈上的最前端,然後依次是上一級函式的 活動物件,… 最後直到全域性執行環境
當函式執行完畢時,函式中的變數都會被銷燬,只保留全域性作用域的 活動物件,但是由於閉包函式的作用域鏈上擁有對其鏈上一系列函式的 活動 物件的引用,所以這些函式的 活動 物件
不會被銷燬,會被保留在記憶體中
閉包與變數
閉包儲存的是整個 活動 物件,而不是某個變數
for(var i=0; i<5; i++){ setTimeout(function(){ console.log(i); },1000*i); }
這道經典的面試題,其會每秒列印一個 5
,原因就是 其實 定時器裡的函式就是一個閉包,它會在同步程式碼執行完畢之後執行,其作用域鏈中儲存了當前函式環境的 活動物件,其中 i
的值在當前函式執行結束之後變為了 5
,然後定時器閉包函式開始執行,然後就會每秒列印一個 5
想要獲得 0, 1, 2, 3, 4
這種列印結果,就得要定時器函式能夠記住當前 i
的 值,可以使用閉包解決( 函式的引數都是值傳遞的 )
for(var i=0; i<5; i++){ setTimeout(function(i){ return function(){ console.log(i); } }(i),1000*i); }
關於 this
this
指向是在函式執行的時候才確定的,所以使用閉包的時候 可能出現問題,只要沒有顯示的呼叫者,或者使用 apply, call, bind
指定呼叫者,則 this
都會指向全域性物件
模仿塊級作用域
ES6 之前,js 是沒有塊級作用域的,這意味著在 if..else、for、while
等語句中宣告的變數會成為當前函式或者全域性的變數,即在這些程式碼塊之外的地方,也能訪問到在塊中定義的變數
變數提升
JS 會在函式執行的時候將函式宣告,變數宣告提升到函式的最頂部先執行( 使用 var
宣告 ),函式優先,如果遇到相同的名字,則會忽略後一個,但是如果在宣告的地方有賦值操作,賦值操作還是會進行,只不過宣告被忽略
可以使用匿名函式來模仿塊級作用域
(function(){
// 這裡的 a 不會被外界訪問到
var a = 1
})()
函式表示式後面可以跟圓括號,可以使用圓括號將匿名函式體包裹起來轉換成函式表示式
(function(){ }) // => 函式表示式