遞歸
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
this
指向是在函數執行的時候才确定的,是以使用閉包的時候 可能出現問題,隻要沒有顯示的調用者,或者使用
apply, call, bind
指定調用者,則
this
都會指向全局對象
模仿塊級作用域
ES6 之前,js 是沒有塊級作用域的,這意味着在
if..else、for、while
等語句中聲明的變量會成為目前函數或者全局的變量,即在這些代碼塊之外的地方,也能通路到在塊中定義的變量
變量提升
JS 會在函數執行的時候将函數聲明,變量聲明提升到函數的最頂部先執行( 使用
var
聲明 ),函數優先,如果遇到相同的名字,則會忽略後一個,但是如果在聲明的地方有指派操作,指派操作還是會進行,隻不過聲明被忽略
可以使用匿名函數來模仿塊級作用域
(function(){
// 這裡的 a 不會被外界通路到
var a = 1
})()
函數表達式後面可以跟圓括号,可以使用圓括号将匿名函數體包裹起來轉換成函數表達式
(function(){ }) // => 函數表達式