1. 程式人生 > >JS高程 -- chapter( 函式表示式 )

JS高程 -- chapter( 函式表示式 )

遞迴

var factorial = (function f(n){
	if(n<=1){
		return 1;
	}else{
		return n*f(n-1);
	}
})

上面這段程式碼使用具名函式表示式來建立遞迴函式,保證 factorial 變數即使指向別的物件也能正確執行遞迴。

( ) 中是什麼,它就返回什麼,若是函式,則其命名在全域性是無法訪問的,只有賦值給一個變數才能訪問( 參考立即執行函式 )

閉包

閉包 是指有權訪問另一個函式作用域中變數的函式

作用域鏈細節:

  1. 函式 建立 的時候,會建立一個執行環境( 執行上下文 ),以及確定作用域鏈,建立 變數 物件
  2. 函式 執行
    的時候, 變數 物件轉換為 活動 物件arguments 物件和函式內宣告的 變數 的賦值,初始化 活動 物件
  3. 作用域鏈中,當前函式的 活動 物件 處於鏈上的最前端,然後依次是上一級函式的 活動物件,… 最後直到全域性執行環境

當函式執行完畢時,函式中的變數都會被銷燬,只保留全域性作用域的 活動物件,但是由於閉包函式的作用域鏈上擁有對其鏈上一系列函式的 活動 物件的引用,所以這些函式的 活動 物件 不會被銷燬,會被保留在記憶體中

閉包與變數

閉包儲存的是整個 活動 物件,而不是某個變數

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(){ }) // => 函式表示式