ECMAScript學習筆記(八)——函式表示式
遞迴
在ECMAScript中,通過名字進行的遞迴,會由於函式被賦值到其他變數上,而導致錯誤:
function factorial(num) { if (num <= 1) { return 1; } else { return num * factorial(num - 1); } } var anotherFactorial = factorial; factorial = null; alert(anotherFactorial); // error
於是,應該這樣解決:
function factorial(num) { if(num <= 1) { return 1; } else { return num * arguments.callee(num - 1); } }
函式的arguments的callee物件指向了正在執行的函式的指標。
但是在嚴格模式下,是不能使用callee物件的。於是,可以使用明明函式表示式來達成相同的結果:
var factorial = (function f(num) { if(num <= 1) { return 1; } else { return f(num - 1); } })
上述程式碼,建立了一個名為f()的明明函式表示式。然後將它賦值給了factorial。這樣一來,就算把函式賦值給了另一個變數,函式的名字f依然有效。
閉包
閉包是指,有權訪問另一個函式作用域中的變數的函式。
建立閉包的常用方式,就是在一個函式內部建立另一個函式:
function createComparisonFunction(propertyName) { return function(object1, object2) { // 此處的兩行程式碼,訪問了外部函式的propertyName變數 var value1 = object1[propertyName]; var value2 = object2[propertyName]; if(value1 < value2) { return -1; } else if (value1 > value2) { return 1; } else { return 0; } } }
上述的:
var value1 = object1[propertyName]; var value2 = object2[propertyName];
這兩行程式碼,訪問來外部函式的propertyName變數。並且,即時這個內部函式被返回了,並且被其他地方呼叫了,它依舊可以訪問propertyName變數。
這是因為,內部函式的作用域鏈中包含了createComparisonFunction()的作用域。
閉包與變數
閉包,只能取得包含函式中任何變數的最後一個值。意思就是說,閉包中獲取的外部函式的變數的值,應該就它的最新的值。
function createFunction() { var result = new Array(); for(var i = 0; i < 10; i++) { result[i] = function() { return i; } } return result; }
例如,以上程式碼中,每個函式都會返回10。因為,上述createFunction的活動物件引用的i是同一個變數。
我們可以這樣做,來強制讓閉包的行為符合預期:
function createFunctions () { var result = new Array(); for (var i = 0; i < 10; i++) { result[i] = function(num) { return function() { return num; } }(i); } return result; }