天天看點

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