JavaScript第七章 函數(shù)表達(dá)式

函數(shù)表達(dá)式是 JavaScript中的一個(gè)既強(qiáng)大又容易令人困惑的特性趴拧。第 5章曾介紹過婆誓,定義函數(shù)的 方式有兩種:一種是函數(shù)聲明,另一種就是函數(shù)表達(dá)式捞蛋。函數(shù)聲明的語法是這樣的。

function functionName(arg0, arg1, arg2) {
     //函數(shù)體 
} 

關(guān)于函數(shù)聲明柬姚,它的一個(gè)重要特征就是函數(shù)聲明提升(function declaration hoisting)拟杉,意思是在執(zhí)行 代碼之前會(huì)先讀取函數(shù)聲明。這就意味著可以把函數(shù)聲明放在調(diào)用它的語句后面量承。

第二種創(chuàng)建函數(shù)的方式是使用函數(shù)表達(dá)式搬设。函數(shù)表達(dá)式有幾種不同的語法形式。下面是常見的一 種形式撕捍。

var functionName = function(arg0, arg1, arg2){ 
    //函數(shù)體
  }; 

這種形式看起來好像是常規(guī)的變量賦值語句拿穴,即創(chuàng)建一個(gè)函數(shù)并將它賦值給變量 functionName。 這種情況下創(chuàng)建的函數(shù)叫做匿名函數(shù)(anonymous function)忧风,因?yàn)?function 關(guān)鍵字后面沒有標(biāo)識(shí)符贞言。 (匿名函數(shù)有時(shí)候也叫拉姆達(dá)函數(shù)。)匿名函數(shù)的 name 屬性是空字符串阀蒂。 函數(shù)表達(dá)式與其他表達(dá)式一樣,在使用前必須先賦值弟蚀。

遞歸

遞歸函數(shù)是在一個(gè)函數(shù)通過名字調(diào)用自身的情況下構(gòu)成的蚤霞,如下所示。

function factorial(num){ 
    if (num <= 1){ 
        return 1;
     } else { 
        return num * factorial(num-1);
     }
} 

我們知道义钉,arguments.callee 是一個(gè)指向正在執(zhí)行的函數(shù)的指針昧绣,因此可以用它來實(shí)現(xiàn)對(duì)函數(shù) 的遞歸調(diào)用,例如:

function factorial(num){

     if (num <= 1){ 

        return 1;

     } else {

         return num * arguments.callee(num-1);

     } 

}  

加粗的代碼顯示捶闸,通過使用 arguments.callee 代替函數(shù)名夜畴,可以確保無論怎樣調(diào)用函數(shù)都不會(huì) 出問題。因此删壮,在編寫遞歸函數(shù)時(shí)贪绘,使用 arguments.callee 總比使用函數(shù)名更保險(xiǎn)。

閉包

有不少開發(fā)人員總是搞不清匿名函數(shù)和閉包這兩個(gè)概念央碟,因此經(jīng)乘肮啵混用。閉包是指有權(quán)訪問另一個(gè) 函數(shù)作用域中的變量的函數(shù)亿虽。創(chuàng)建閉包的常見方式菱涤,就是在一個(gè)函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù),仍以前面的 createComparisonFunction()函數(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;
         } 
    }; 
} 

在這個(gè)例子中,突出的那兩行代碼是內(nèi)部函數(shù)(一個(gè)匿名函數(shù))中的代碼收毫,這兩行代碼訪問了外部 函數(shù)中的變量 propertyName攻走。即使這個(gè)內(nèi)部函數(shù)被返回了殷勘,而且是在其他地方被調(diào)用了,但它仍然可 以訪問變量 propertyName陋气。之所以還能夠訪問這個(gè)變量劳吠,是因?yàn)閮?nèi)部函數(shù)的作用域鏈中包含 createComparisonFunction()的作用域。要徹底搞清楚其中的細(xì)節(jié)巩趁,必須從理解函數(shù)被調(diào)用的時(shí)候 都會(huì)發(fā)生什么入手痒玩。 第 4章介紹了作用域鏈的概念。而有關(guān)如何創(chuàng)建作用域鏈以及作用域鏈有什么作用的細(xì)節(jié)议慰,對(duì)徹底 理解閉包至關(guān)重要蠢古。當(dāng)某個(gè)函數(shù)被調(diào)用時(shí),會(huì)創(chuàng)建一個(gè)執(zhí)行環(huán)境(execution context)及相應(yīng)的作用域鏈别凹。 然后草讶,使用 arguments 和其他命名參數(shù)的值來初始化函數(shù)的活動(dòng)對(duì)象(activation object)。但在作用域 鏈中炉菲,外部函數(shù)的活動(dòng)對(duì)象始終處于第二位堕战,外部函數(shù)的外部函數(shù)的活動(dòng)對(duì)象處于第三位,……直至作為作用域鏈終點(diǎn)的全局執(zhí)行環(huán)境拍霜。

閉包與變量

作用域鏈的這種配置機(jī)制引出了一個(gè)值得注意的副作用嘱丢,即閉包只能取得包含函數(shù)中任何變量的 后一個(gè)值。別忘了閉包所保存的是整個(gè)變量對(duì)象祠饺,而不是某個(gè)特殊的變量越驻。

關(guān)于this對(duì)象

在閉包中使用 this 對(duì)象也可能會(huì)導(dǎo)致一些問題。我們知道道偷,this 對(duì)象是在運(yùn)行時(shí)基于函數(shù)的執(zhí) 行環(huán)境綁定的:在全局函數(shù)中缀旁,this 等于 window,而當(dāng)函數(shù)被作為某個(gè)對(duì)象的方法調(diào)用時(shí)勺鸦,this 等 于那個(gè)對(duì)象并巍。不過,匿名函數(shù)的執(zhí)行環(huán)境具有全局性换途,因此其 this 對(duì)象通常指向 window①履澳。但有時(shí)候 由于編寫閉包的方式不同,這一點(diǎn)可能不會(huì)那么明顯怀跛。

內(nèi)存泄漏

由于 IE9之前的版本對(duì) JScript對(duì)象和 COM對(duì)象使用不同的垃圾收集例程(第 4章曾經(jīng)討論過)距贷,

因此閉包在 IE 的這些版本中會(huì)導(dǎo)致一些特殊的問題。具體來說吻谋,如果閉包的作用域鏈中保存著一個(gè) HTML元素忠蝗,那么就意味著該元素將無法被銷毀。

模仿塊級(jí)作用域

JavaScript從來不會(huì)告訴你是否多次聲明了同一個(gè)變量漓拾;遇到這種情況阁最,它只會(huì)對(duì)后續(xù)的聲明視而不 見(不過戒祠,它會(huì)執(zhí)行后續(xù)聲明中的變量初始化)。匿名函數(shù)可以用來模仿塊級(jí)作用域并避免這個(gè)問題速种。 用作塊級(jí)作用域(通常稱為私有作用域)的匿名函數(shù)的語法如下所示姜盈。

(function(){ //這里是塊級(jí)作用域 })();
以上代碼定義并立即調(diào)用了一個(gè)匿名函數(shù)。將函數(shù)聲明包含在一對(duì)圓括號(hào)中配阵,表示它實(shí)際上是一個(gè) 函數(shù)表達(dá)式馏颂。而緊隨其后的另一對(duì)圓括號(hào)會(huì)立即調(diào)用這個(gè)函數(shù)。

私有變量

嚴(yán)格來講棋傍,JavaScript 中沒有私有成員的概念救拉;所有對(duì)象屬性都是公有的。不過瘫拣,倒是有一個(gè)私有 變量的概念亿絮。任何在函數(shù)中定義的變量,都可以認(rèn)為是私有變量麸拄,因?yàn)椴荒茉诤瘮?shù)的外部訪問這些變量派昧。 私有變量包括函數(shù)的參數(shù)、局部變量和在函數(shù)內(nèi)部定義的其他函數(shù)拢切。

我們把有權(quán)訪問私有變量和私有函數(shù)的公有方法稱為特權(quán)方法(privileged method)斗锭。有兩種在對(duì)象 上創(chuàng)建特權(quán)方法的方式。第一種是在構(gòu)造函數(shù)中定義特權(quán)方法

這個(gè)模式在構(gòu)造函數(shù)內(nèi)部定義了所有私有變量和函數(shù)失球。然后,又繼續(xù)創(chuàng)建了能夠訪問這些私有成員 的特權(quán)方法帮毁。能夠在構(gòu)造函數(shù)中定義特權(quán)方法实苞,是因?yàn)樘貦?quán)方法作為閉包有權(quán)訪問在構(gòu)造函數(shù)中定義的 所有變量和函數(shù)。

靜態(tài)私有變量

通過在私有作用域中定義私有變量或函數(shù)烈疚,同樣也可以創(chuàng)建特權(quán)方法

這個(gè)模式創(chuàng)建了一個(gè)私有作用域黔牵,并在其中封裝了一個(gè)構(gòu)造函數(shù)及相應(yīng)的方法。在私有作用域中爷肝, 首先定義了私有變量和私有函數(shù)猾浦,然后又定義了構(gòu)造函數(shù)及其公有方法。公有方法是在原型上定義的灯抛, 這一點(diǎn)體現(xiàn)了典型的原型模式金赦。需要注意的是,這個(gè)模式在定義構(gòu)造函數(shù)時(shí)并沒有使用函數(shù)聲明对嚼,而是 使用了函數(shù)表達(dá)式夹抗。函數(shù)聲明只能創(chuàng)建局部函數(shù),但那并不是我們想要的纵竖。出于同樣的原因漠烧,我們也沒 有在聲明 MyObject 時(shí)使用 var 關(guān)鍵字杏愤。記住:初始化未經(jīng)聲明的變量已脓,總是會(huì)創(chuàng)建一個(gè)全局變量珊楼。 因此,MyObject 就成了一個(gè)全局變量度液,能夠在私有作用域之外被訪問到厕宗。但也要知道,在嚴(yán)格模式下 給未經(jīng)聲明的變量賦值會(huì)導(dǎo)致錯(cuò)誤恨诱。

這個(gè)模式與在構(gòu)造函數(shù)中定義特權(quán)方法的主要區(qū)別媳瞪,就在于私有變量和函數(shù)是由實(shí)例共享的。由于 特權(quán)方法是在原型上定義的照宝,因此所有實(shí)例都使用同一個(gè)函數(shù)蛇受。而這個(gè)特權(quán)方法,作為一個(gè)閉包厕鹃,總是 保存著對(duì)包含作用域的引用

模塊模式

前面的模式是用于為自定義類型創(chuàng)建私有變量和特權(quán)方法的兢仰。而道格拉斯所說的模塊模式(module pattern)則是為單例創(chuàng)建私有變量和特權(quán)方法。所謂單例(singleton)剂碴,指的就是只有一個(gè)實(shí)例的對(duì)象把将。 按照慣例,JavaScript是以對(duì)象字面量的方式來創(chuàng)建單例對(duì)象的忆矛。

這個(gè)模塊模式使用了一個(gè)返回對(duì)象的匿名函數(shù)察蹲。在這個(gè)匿名函數(shù)內(nèi)部,首先定義了私有變量和函數(shù)催训。 然后洽议,將一個(gè)對(duì)象字面量作為函數(shù)的值返回。返回的對(duì)象字面量中只包含可以公開的屬性和方法漫拭。由于 這個(gè)對(duì)象是在匿名函數(shù)內(nèi)部定義的亚兄,因此它的公有方法有權(quán)訪問私有變量和函數(shù)。從本質(zhì)上來講采驻,這個(gè) 對(duì)象字面量定義的是單例的公共接口审胚。這種模式在需要對(duì)單例進(jìn)行某些初始化,同時(shí)又需要維護(hù)其私有 變量時(shí)是非常有用的

簡(jiǎn)言之礼旅,如果必須創(chuàng)建一個(gè)對(duì)象并以某些數(shù)據(jù)對(duì)其進(jìn)行初始化膳叨,同時(shí)還要公開一些能夠訪問這些私有 數(shù)據(jù)的方法,那么就可以使用模塊模式痘系。以這種模式創(chuàng)建的每個(gè)單例都是 Object 的實(shí)例懒鉴,因?yàn)榻K要通 過一個(gè)對(duì)象字面量來表示它。事實(shí)上,這也沒有什么临谱;畢竟璃俗,單例通常都是作為全局對(duì)象存在的,我們不 會(huì)將它傳遞給一個(gè)函數(shù)悉默。因此城豁,也就沒有什么必要使用 instanceof 操作符來檢查其對(duì)象類型了。

增強(qiáng)的模塊模式

有人進(jìn)一步改進(jìn)了模塊模式抄课,即在返回對(duì)象之前加入對(duì)其增強(qiáng)的代碼唱星。這種增強(qiáng)的模塊模式適合那 些單例必須是某種類型的實(shí)例,同時(shí)還必須添加某些屬性和(或)方法對(duì)其加以增強(qiáng)的情況跟磨。

var application = function(){ 

//私有變量和函數(shù)
var components = new Array(); 

//初始化
components.push(new BaseComponent()); 

//創(chuàng)建 application 的一個(gè)局部副本
var app = new BaseComponent(); 
 
//公共接口
app.getComponentCount = function(){
return components.length;
}; 
app.registerComponent = function(component){
if (typeof component == "object"){
components.push(component);
}
}; 

//返回這個(gè)副本 
return app; }(); 

ModuleAugmentationPatternExample01.htm 在這個(gè)重寫后的應(yīng)用程序(application)單例中间聊,首先也是像前面例子中一樣定義了私有變量。主 要的不同之處在于命名變量 app 的創(chuàng)建過程抵拘,因?yàn)樗仨毷?BaseComponent 的實(shí)例哎榴。這個(gè)實(shí)例實(shí)際上 是 application 對(duì)象的局部變量版。此后僵蛛,我們又為 app 對(duì)象添加了能夠訪問私有變量的公有方法尚蝌。 后一步是返回 app 對(duì)象,結(jié)果仍然是將它賦值給全局變量 application充尉。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末飘言,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子驼侠,更是在濱河造成了極大的恐慌姿鸿,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件倒源,死亡現(xiàn)場(chǎng)離奇詭異苛预,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)相速,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鲜锚,“玉大人突诬,你說我怎么就攤上這事∥叻保” “怎么了旺隙?”我有些...
    開封第一講書人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)骏令。 經(jīng)常有香客問我蔬捷,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任周拐,我火速辦了婚禮铡俐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘妥粟。我一直安慰自己审丘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開白布勾给。 她就那樣靜靜地躺著滩报,像睡著了一般。 火紅的嫁衣襯著肌膚如雪播急。 梳的紋絲不亂的頭發(fā)上脓钾,一...
    開封第一講書人閱讀 52,255評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音桩警,去河邊找鬼可训。 笑死,一個(gè)胖子當(dāng)著我的面吹牛生真,可吹牛的內(nèi)容都是我干的沉噩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼柱蟀,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼川蒙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起长已,我...
    開封第一講書人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤畜眨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后术瓮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體康聂,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年胞四,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了恬汁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡辜伟,死狀恐怖氓侧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情导狡,我是刑警寧澤约巷,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站旱捧,受9級(jí)特大地震影響独郎,放射性物質(zhì)發(fā)生泄漏踩麦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一氓癌、第九天 我趴在偏房一處隱蔽的房頂上張望谓谦。 院中可真熱鬧,春花似錦顽铸、人聲如沸茁计。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽星压。三九已至,卻和暖如春鬼譬,著一層夾襖步出監(jiān)牢的瞬間娜膘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工优质, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留竣贪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓巩螃,卻偏偏與公主長(zhǎng)得像演怎,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子避乏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • ??函數(shù)表達(dá)式是 JavaScript 中的一個(gè)既強(qiáng)大有容易令人困惑的特性拍皮。定義函數(shù)的的方式有兩種: 函數(shù)聲明歹叮; ...
    霜天曉閱讀 819評(píng)論 0 1
  • 定義函數(shù)的方式有兩種:函數(shù)聲明和函數(shù)表達(dá)式。 函數(shù)聲明的一個(gè)重要特征就是函數(shù)聲明提升铆帽,意思是在執(zhí)行代碼前會(huì)先讀取函...
    oWSQo閱讀 668評(píng)論 0 0
  • 本文主要介紹咆耿,函數(shù)表達(dá)式特征、使用函數(shù)實(shí)現(xiàn)遞歸爹橱、使用閉包定義私有變量萨螺。 函數(shù)表達(dá)式特征 函數(shù)表達(dá)式是JavaScr...
    了凡和纖風(fēng)閱讀 1,139評(píng)論 0 0
  • 第七章:函數(shù)表達(dá)式 本章內(nèi)容: 函數(shù)表達(dá)式的特征 使用函數(shù)實(shí)現(xiàn)遞歸 使用閉包定義私有變量 定義函數(shù)的方式有兩種,一...
    穿牛仔褲的蚊子閱讀 375評(píng)論 0 1
  • 伴讀分享 歲月漫長(zhǎng)愧驱,人生苦短慰技,幾十年的光陰,彈指一揮間冯键,夢(mèng)里那些荒廢的時(shí)光惹盼,早已刻上干枯的年輪庸汗”谷罚回顧過去,我們一路...
    瑣珥閱讀 269評(píng)論 0 0