Chapter 7 函數(shù)表達(dá)式
基本概念
-
函數(shù)聲明提升:執(zhí)行代碼前會(huì)先讀取函數(shù)聲明臂外。
- 函數(shù)聲明
function sayHi() { alert("Hi!"); }
- 函數(shù)表達(dá)式
var sayHi = function() { alert("Hi!"); }
匿名函數(shù):function關(guān)鍵字后沒有標(biāo)識(shí)符配猫。
不要在條件語句中聲明函數(shù),因?yàn)闂l件語句可能會(huì)被忽略拼苍。但是可以使用函數(shù)表達(dá)式独令。
遞歸
- 函數(shù)名遞歸(略)
- 使用arguments.callee指針實(shí)現(xiàn)遞歸
function factorial(num) {
if (num < 1) {
return 1;
} else {
return num * arguments.callee(num - 1);
}
}
- 在嚴(yán)格模式下愕秫,不能通過腳本訪問arguments.callee,可以通過如下方法
var factorial = (function f(num) {
if (num <= 1) {
return 1;
} else {
return num * f(num - 1);
}
});
閉包
- 定義:閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)挟炬。
- 創(chuàng)建閉包的常見方式鸥滨,是在一個(gè)函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù)
function createComparisonFunction(propertyName) {
return function(object1, object2) {
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if (value1 < value2) return -1;
else if (value1 < value2) return 1;
else return 0;
}
}
作用域鏈:當(dāng)某個(gè)函數(shù)被調(diào)用時(shí),會(huì)創(chuàng)建一個(gè)執(zhí)行環(huán)境及相應(yīng)的作用域鏈谤祖。然后婿滓,使用arguments和其他命名參數(shù)的值來初始化函數(shù)的活動(dòng)對象。但在作用域鏈中粥喜,使用arguments和其他命名參數(shù)的值來初始化函數(shù)的活動(dòng)對象凸主。但是在作用域鏈中,外部函數(shù)的活動(dòng)對象始終處于第二位额湘,外部函數(shù)的外部函數(shù)的活動(dòng)對象處于第三位.....直到作為作用域終點(diǎn)的全局執(zhí)行環(huán)境卿吐。
作用域鏈的本質(zhì)是一個(gè)指向變量對象的指針列表旁舰,它只引用但不實(shí)際包含對象。
-
本地活動(dòng)對象 & 全局變量對象
- 在另一個(gè)函數(shù)內(nèi)部定義的函數(shù)會(huì)將包含函數(shù)的活動(dòng)對象添加到它的作用域鏈中嗡官。包含函數(shù)執(zhí)行完畢后箭窜,其活動(dòng)對象不會(huì)被銷毀,因?yàn)楸话哪涿瘮?shù)的作用域鏈仍然在引用這個(gè)活動(dòng)對象衍腥。直到匿名函數(shù)被銷毀后磺樱,該函數(shù)的活動(dòng)對象才會(huì)被銷毀。(仔細(xì)看書)
由于閉包會(huì)攜帶包含它的函數(shù)作用域婆咸,因此會(huì)比其他函數(shù)占用更多的內(nèi)存竹捉。
-
閉包與變量
- 閉包只能取得包含函數(shù)中任何變量的最后一個(gè)值
function createFunctions() { var result = new Array(); for(var i = 0; i < 10; i++) { result[i] = function() { return i; } } return result; }
- createFunctions()函數(shù)會(huì)返回一個(gè)函數(shù)數(shù)組,并且每個(gè)函數(shù)都返回10尚骄。因?yàn)槊總€(gè)函數(shù)的作用域鏈中都保存著createFunctions()函數(shù)的活動(dòng)對象活孩,所以它們引用的都是同一個(gè)變量i。當(dāng)createFunctions()函數(shù)返回后乖仇,變量i的值是10憾儒,此時(shí)每個(gè)函數(shù)都引用著保存變量i的同一個(gè)變量對象,所以在每個(gè)函數(shù)內(nèi)部i的值都是10乃沙。解決方法如下
function createFunctions() { var result = new Array(); for(var i = 0; i < 10; i++) { result[i] = function(num) { return function() { return num; }; }(i); } return result; }
-
關(guān)于this對象
- 匿名函數(shù)的執(zhí)行環(huán)境具有全局性起趾,因此其this通常指向window。
var name = 'Lawrence'; var object = { name: 'Qwerty', getName: function() { return function() { return this.name; } } }; alert(object.getName()); // 'Lawrence'
- 匿名函數(shù)沒有取得其包含作用域的this對象:每個(gè)函數(shù)在被調(diào)用的時(shí)候都會(huì)自動(dòng)取得兩個(gè)特殊變量警儒,this和arguments训裆。內(nèi)部函數(shù)在搜索這兩個(gè)變量時(shí),只會(huì)搜索到其活動(dòng)對象為止蜀铲,因此永遠(yuǎn)不可能直接訪問外部函數(shù)中的這兩個(gè)變量边琉。解決方法:把外部作用域中的this對象保存在一個(gè)閉包能夠訪問到的變量里。
var name = 'Lawrence'; var object = { name: 'Qwerty', getName: function() { var that = this; return function() { return that.name; } } }; alert(object.getName()); // 'Qwerty'
- arguments也存在同樣的問題
-
內(nèi)存泄漏
- IE9-的版本中记劝,如果閉包的作用域鏈中保存著一個(gè)HTML元素变姨,那么就意味著該元素將無法被銷毀。(具體見書)
模仿塊級(jí)作用域
- Java沒有塊級(jí)作用域厌丑。在塊語句中定義的變量定欧,實(shí)際上是在包含函數(shù)中而非語句中創(chuàng)建的。
- 可以通過匿名函數(shù)模仿塊級(jí)作用域
(function() {
// 這里是塊級(jí)作用域
})(); // 包裹function的括號(hào)是必需的怒竿。
- 這種做法可以減少閉包占用內(nèi)存的問題砍鸠,因?yàn)闆]有指向匿名函數(shù)的引用。
私有變量
JavaScript沒有私有成員的概念耕驰,所有對象的屬性都是公有的爷辱。但是有私有變量的概念。任何在函數(shù)中定義的變量,都可以認(rèn)為是私有變量饭弓。私有變量包括函數(shù)參數(shù)巩检、局部變量和在函數(shù)內(nèi)部定義的其他函數(shù)。示启、
-
有權(quán)訪問私有變量和私有函數(shù)的公有方法稱為特權(quán)方法兢哭。有兩種在對象上創(chuàng)建特權(quán)方法的方式
- 在構(gòu)造函數(shù)中定義特權(quán)方法
function MyObject() { var privateVariable = 10; function privateFunction() { return false; } // 特權(quán)方法 this.publicMethod = function() { privateVariable++; return privateFunction(); }; }
利用私有和特權(quán)成員,可以隱藏那些不應(yīng)該被直接修改的數(shù)據(jù)夫嗓。
-
靜態(tài)私有變量
- 通過在私有作用域中定義私有變量或函數(shù)迟螺,同樣可以創(chuàng)建特權(quán)方法,其基本模式如下
(function() {
var privateVariable = 10;
function privateFunction() {
return false;
}
MyObject = function() {
}; // 全局變量
// 特權(quán)方法
MyObject.prototype.publicMethod = function() {
privateVariable++;
return privateFunction();
};
})();
```
* 多查找作用域鏈中的一個(gè)層次舍咖,就會(huì)在一定程度上影響查找速度矩父。
-
模塊模式
- 為單例創(chuàng)建私有變量和特權(quán)方法。JavaScript以對象字面量創(chuàng)建單例對象排霉。模塊模式通過為單例添加私有變量和特權(quán)方法使其得到增強(qiáng)
var singleton = function() { var privateVariable = 10; function privateFunction() { return false; } //特權(quán)方法和屬性 return { publicProperty: true; publicMethod: function() { privateVariable++; return privateFunction(); } }; }();
* 這種模式在需要對單例進(jìn)行某些初始化窍株,同時(shí)又需要維護(hù)其私有變量時(shí)是非常有用的。
```
var application = function() {
var components = new Array();
components.push(new BaseComponent());
return {
getComponentCount: function() {
return components.length;
},
registerComponent: function(component) {
if(typeof component == 'object') {
components.push(component);
}
}
};
}();
```
-
增強(qiáng)的模塊模式
- 在返回對象之前加入對其增強(qiáng)的代碼攻柠。
var singleton = function() { var privateVariable = 10; function privateFunction() { return false; } var object = new CustomType(); object.publicProperty = true; object.publicMethod = function() { privateVariable++; return privateFunction(); }; return object; }();