JavaScript函數表達式

定義函數的兩種方式

函數聲明:函數聲明提升,在執行代碼之前會先讀取函數聲明,意味著可以把函數聲明放在調用它的語句后面。

函數表達式:不會提升

遞歸

function factorial(num){

if (num <= 1){

? return 1;

} else {

return num * factorial(num - 1);

}

var anotherFactorial = factorial;

factorial = null;

alert(anotherFactorial(4));//error

將factorial設置為null,結果指向原始函數的引用只剩下一個。接下來調用anotherFactorial時,由于必需執行factorial,而factorial已經不再是函數,導致出錯。可以使用arguments.callee解決。或者:

var factorial = (function f(num){

? if (num <= 1){

? return 1;

} else {

? ?return num * f (num -1);

}

});

閉包

閉包是有權訪問另一個函數作用域中的變量的函數。創建閉包的常見方式,就是在一個函數內部創建另一個函數。

function createComparisonFunction(propertyName){

? return function(object1, object2){

? ? ? var v1 = object1[propertyName];

? ? ? ?var v2 = object2[propertyName];

? ? ? if (v1 < v2){

? ? ? ? ? return -1;

? ? ? ?} else if ( v1 > v2){

? ? ? ? ? return 1;

? ? ? } else {

? ? ? ? return 0;

? ? ? ?}

};

}

當函數第一次被調用時,會創建一個執行環境(EC)及相應的作用域鏈,并把作用域鏈賦值給一個特殊的內部屬性[[Scope]]。然后使用this、arguments和其他命名參數的值來初始化函數的活動對象(AO)。但在作用域鏈中,外部函數的活動對象始終處于第二位,外部函數的外部函數的活動對象處于第三位,直至作為作用域鏈重點的 全局執行環境。

function compare(v1, v2){

? if (v1 < v2){

? ? return -1;

} else if ( v1 > v2){

? ? return 1;

} else {

? ? ?return 0;

}

}

var result = compare(1, 2);

第一次調用compare時,會創建一個包含this,arguments、v1和v2的活動對象。全局執行環境的變量對象(包含this、result和compare)在compare執行環境的作用域鏈中則處于第二位。

每個執行環境都有一個表示變量的對象--變量對象(VO)。全局環境的變量對象始終存在,而像compare函數這樣的局部環境的變量對象,則只在函數執行的過程中存在。在創建compare函數時,會創建一個預先包含全局變量對象的作用域鏈,這個作用域鏈被保存在北部的[[Scope]]屬性中。當調用compare函數時,會為函數創建一個執行環境,然后通過復制函數的[[Scope]]屬性中的對象構建起執行環境的作用域鏈。此后,又有一個活動對象(在此作為變量對象使用)被創建并被推入執行環境作用域鏈的前端。對于compare函數的執行環境,其作用域鏈中包含兩變量對象:本地活動對象和全局變量對象。作用域鏈本質上是一個指向變量對象的指針列表,它只引用但不實際包含變量對象。

閉包的情況不同。在另一個函數內部定義的函數會將包含函數的活動對象添加到他的作用域鏈中。在createCompareFunction函數內部定義的匿名函數的作用域鏈中,實際上將包含外部函數createCompareFunction的活動對象。并且createCompareFunction函數在執行完畢后,其活動對象也不會被銷毀,因為匿名函數的作用域鏈仍然在引用這個活動對象。當createCompareFunction函數返回后,只是其執行環境的作用域鏈被銷毀,但它的活動對象仍然會留在內存中;直到匿名函數被銷毀后它的活動對象才會被銷毀。

閉包與變量

作用域鏈的這種配置機制引出了一個值得注意的副作用,即閉包只能取得包含函數中任何變量的最后一個值。閉包所保存的是整個變量對象,而不是某個特殊的變量。

function createFunction(){

? var result = new Array();

? for (var i = 0; i < 10; ++i){

? ?result[i] = function(){?

? ? ? ? ? ?return i;?

? ? ? ? ? ? ?}

? ?}

? ?return result;

}

改寫為

function createFunction(){

? ? var result = new Array();

? ? for (var i = 0; i < 10; ++i) {

? ? ? ? ? result[i] = function(num){

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return function() {

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?return num;

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?};

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }(i);

? ? }

? ? return result;

}

關于this

this對象是在運行時基于函數的執行環境綁定的:全局環境中,this等于window;而當函數被作為某個對象的方法調用時,this等于那個對象。不過,匿名函數的執行環境具有全局性,因此其this對象通常指向window。每個函數在被調用時,其活動對象都會自動取得兩個特殊變量:this和arguments。內部函數在搜索這兩個變量時,只會搜索到其活動對象為止,因此永遠不可能直接訪問外部函數中的這兩個變量。不過,把外部作用域中的this對象保存在一個閉包能夠訪問到的變量里。就可以讓閉包訪問該對象了。

內存泄漏

模仿塊級作用域

私有變量

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容