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

2. 模仿塊級(jí)作用域

前面我們說(shuō)到碍舍,JavaScript中沒(méi)有塊級(jí)作用域的概念片橡。這意味著在塊語(yǔ)句中定義的變量,實(shí)際上是在包含函數(shù)中而非語(yǔ)句中創(chuàng)建的捧书。下面是一個(gè)例子:

function outputNumber(count){
    for (var i=0; i<count; i++){
        alert(i);
    }
    alert(i);
}

上面的代碼是完全可執(zhí)行的,因?yàn)樵趂or語(yǔ)句中定義的變量i是出于函數(shù)作用域的骤星,這也說(shuō)明了JavaScript中沒(méi)有塊級(jí)作用域经瓷。另外,JavaScript從來(lái)不會(huì)在意是否多次聲明了同一個(gè)變量洞难,如果遇到這種情況(例如在for循環(huán)結(jié)束后舆吮,使用var i;再次定義一個(gè)i變量,也不會(huì)改變i的值,除非你在定義時(shí)顯示的初始化了)歪泳,它只會(huì)直接忽略后續(xù)的聲明萝勤。

我們可以通過(guò)匿名函數(shù)來(lái)模擬塊級(jí)作用域。下面是一個(gè)例子:

function outputNumber(count){
    (function(){
         for (var i=0; i<count; i++){
            alert(i);
         }
    })();
    alert(i);  //error!
}

在這個(gè)重寫后的 outputNumbers()函數(shù)中呐伞,我們?cè)?for 循環(huán)外部插入了一個(gè)私有作用域敌卓。在匿名 函數(shù)中定義的任何變量,都會(huì)在執(zhí)行結(jié)束時(shí)被銷毀伶氢。因此趟径,變量 i 只能在循環(huán)中使用,使用后即被銷毀癣防。 而在私有作用域中能夠訪問(wèn)變量 count蜗巧,是因?yàn)檫@個(gè)匿名函數(shù)是一個(gè)閉包,它能夠訪問(wèn)包含作用域中的 所有變量蕾盯。

3. 私有變量

嚴(yán)格來(lái)說(shuō)幕屹,JavaScript中沒(méi)有私有成員的概念;所有對(duì)象屬性都是公有的级遭。不過(guò)望拖,倒是有一個(gè)私有變量的概念:任何在函數(shù)中定義的變量,都可以認(rèn)為是私有變量挫鸽,因?yàn)椴荒茉诤瘮?shù)外部訪問(wèn)這些變量说敏。私有變量包括函數(shù)參數(shù),局部變量和函數(shù)內(nèi)部定義的其他函數(shù)丢郊。下面是一個(gè)例子:

function MyObject(){
    //私有變量
    var privateVariable  = 10;
    
    //私有函數(shù)
    function privateFunction(){
       return false;
    }

    this.publicMethod = function(){
        privateVariable  ++;
        return privateFunction();
    }
}

我們把有權(quán)訪問(wèn)私有變量和私有函數(shù)的公有方法稱為特權(quán)方法(privileged method)盔沫。上面的例子定義了一個(gè)MyObject引用類型,在構(gòu)造函數(shù)內(nèi)部定義了所有私有變量和函數(shù)枫匾。然后架诞,有定義了一個(gè)名為publicMethod()的特權(quán)方法。能夠在構(gòu)造函數(shù)中定義特權(quán)方法干茉,是因?yàn)樘貦?quán)方法作為閉包有權(quán)訪問(wèn)在構(gòu)造函數(shù)中定義的 所有變量和函數(shù)侈贷。在創(chuàng)建 MyObject 的實(shí)例后,除了使用 publicMethod()這一個(gè)途徑外等脂,沒(méi)有任何辦法可以直接訪問(wèn) privateVariable 和 privateFunction()

函數(shù)中定義特權(quán)方法也有一個(gè)缺點(diǎn)撑蚌,那就是你必須使用構(gòu)造函數(shù)模式來(lái)達(dá)到這個(gè)目的上遥。第 6章曾經(jīng)討論 過(guò)粉楚,構(gòu)造函數(shù)模式的缺點(diǎn)是針對(duì)每個(gè)實(shí)例都會(huì)創(chuàng)建同樣一組新方法,而使用靜態(tài)私有變量來(lái)實(shí)現(xiàn)特權(quán)方 法就可以避免這個(gè)問(wèn)題伟骨。

3.1 靜態(tài)私有變量

通過(guò)在私有作用域中定義私有變量或函數(shù)携狭,同樣也可以創(chuàng)建特權(quán)方法逛腿。下面是一個(gè)例子:

(function(){
     //私有變量
    var privateVariable  = 10;
    
    //私有函數(shù)
    function privateFunction(){
       return false;
    }
    
    //構(gòu)造函數(shù)
    MyObject = function(){
    };
    //特權(quán)方法
    MyObject.prototype.publicMethod = function(){
        privateVariable  ++;
        return privateFunction();
    }
})();

這個(gè)模式創(chuàng)建了一個(gè)私有作用域单默,并在其中封裝了一個(gè)構(gòu)造函數(shù)及相應(yīng)的方法忘瓦。公有方法是在原型上定義的耕皮, 這一點(diǎn)體現(xiàn)了典型的原型模式。需要注意的是汽摹,這個(gè)模式在定義構(gòu)造函數(shù)時(shí)并沒(méi)有使用函數(shù)聲明逼泣,而是 使用了函數(shù)表達(dá)式拉庶。函數(shù)聲明只能創(chuàng)建局部函數(shù)秃励,但那并不是我們想要的夺鲜。出于同樣的原因,我們也沒(méi) 有在聲明 MyObject 時(shí)使用 var 關(guān)鍵字慷蠕。記琢骺弧:初始化未經(jīng)聲明的變量,總是會(huì)創(chuàng)建一個(gè)全局變量剑辫。 因此渠欺,MyObject 就成了一個(gè)全局變量峻堰,能夠在私有作用域之外被訪問(wèn)到。
但也要知道旦万,在嚴(yán)格模式下給未經(jīng)聲明的變量賦值會(huì)導(dǎo)致錯(cuò)誤成艘。

以這種方式創(chuàng)建靜態(tài)私有變量會(huì)因?yàn)槭褂迷湍J蕉鲞M(jìn)代碼復(fù)用(函數(shù)是在對(duì)象實(shí)例間共享的)淆两,但缺點(diǎn)是每個(gè)實(shí)例都沒(méi)有自己的私有變量(屬性也在實(shí)例間共享拂酣,因此屬性全部都是靜態(tài)屬性)婶熬。到底是使用實(shí)例變量,還是靜態(tài)私有變量虽另,終還是要視你的具體需求而定捂刺。

3.2 模塊模式

前面介紹的模式都是用于為自定義類型創(chuàng)建私有變量和特權(quán)方法的募寨,而模塊模式主要用于為單例創(chuàng)建私有變量和特權(quán)方法拔鹰。按照慣例,JavaScript是通過(guò)字面量來(lái)創(chuàng)建單例對(duì)象的:

var singleton = {
    name: "Ivan",
    getName: function(){
        return this.name;  
    }
}

使用字面量方式創(chuàng)建的單例對(duì)象,其屬性都是公開(kāi)的例书。因此就有了模塊模式:

var singleton = function(){
    //私有變量
    var privateVariable  = 10;
    
    //私有函數(shù)
    function privateFunction(){
       return false;
    }

    return {
        publicMethod: function(){
            privateVariable++;
            return privateFunction();
        }
    }
}();

模塊模式使用一個(gè)匿名函數(shù)返回一個(gè)對(duì)象决采。字面量對(duì)象中定義了公開(kāi)屬性和特權(quán)方法树瞭。這種模式在需要對(duì)單例進(jìn)行某些初始化,同時(shí)又需要維護(hù)其私有 變量時(shí)是非常有用的孝偎,例如:

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

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

   //公共
   return {         
      getComponentCount : function(){
          return components.length;         
      }, 
      registerComponent : function(component){
          if (typeof component == "object"){                 
              components.push(component);             
          }         
       }     
    }; 
}(); 

有人進(jìn)一步改進(jìn)了模塊模式衣盾,即在返回對(duì)象之前加入對(duì)其增強(qiáng)的代碼势决。這種增強(qiáng)的模塊模式適合那 些單例必須是某種類型的實(shí)例果复,同時(shí)還必須添加某些屬性和(或)方法對(duì)其加以增強(qiáng)的情況渤昌。來(lái)看下面 的例子:

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;
}();

在這個(gè)重寫后的應(yīng)用程序(application)單例中极颓,首先也是像前面例子中一樣定義了私有變量群嗤。主 要的不同之處在于命名變量 app 的創(chuàng)建過(guò)程,因?yàn)樗仨毷?BaseComponent 的實(shí)例骇径。這個(gè)實(shí)例實(shí)際上 是 application 對(duì)象的局部變量版破衔。此后晰筛,我們又為 app 對(duì)象添加了能夠訪問(wèn)私有變量的公有方法。 后一步是返回 app 對(duì)象读第,結(jié)果仍然是將它賦值給全局變量 application怜瞒。

4. 總結(jié)

在JavaScript中吴汪,函數(shù)表達(dá)式是一種非常重要的技術(shù),使用函數(shù)表達(dá)式無(wú)需對(duì)函數(shù)進(jìn)行命名杆融,從而實(shí)現(xiàn)動(dòng)態(tài)編程擒贸。下面是函數(shù)表達(dá)式的一些顯著特點(diǎn):

  • 函數(shù)聲明要求一定具有名字觉渴,而函數(shù)表達(dá)式則不需要案淋。沒(méi)有名字的函數(shù)表達(dá)式也被稱為匿名函數(shù)。

當(dāng)在函數(shù)內(nèi)部定義了其他函數(shù)時(shí)誉碴,就創(chuàng)建了閉包黔帕。閉包有權(quán)訪問(wèn)包含函數(shù)中的所有變量蹈丸,原理如下;

  • 閉包函數(shù)的作用域鏈包含著自己的活動(dòng)對(duì)象逻杖,包含函數(shù)的變量對(duì)象以及全局函數(shù)的變量對(duì)象。因此能夠沿著作用域鏈訪問(wèn)包含函數(shù)中的變量闻伶。
  • 通常蓝翰,函數(shù)的作用域及其所有變量都將在函數(shù)執(zhí)行完畢后被銷毀。
  • 但是當(dāng)函數(shù)返回一個(gè)閉包時(shí)奇钞,這個(gè)函數(shù)的作用域會(huì)一直存在直到閉包被銷毀為止漂坏。

使用閉包可以在JavaScript中模仿塊級(jí)作用域顶别,要點(diǎn)如下:

  • 定義并立即調(diào)用一個(gè)函數(shù)驯绎,這樣即可以執(zhí)行其中的代碼剩失,又不會(huì)在內(nèi)存中留下該函數(shù)的引用册着。

閉包還可以用于在對(duì)象中創(chuàng)建私有變量甲捏,相關(guān)要點(diǎn)如下:

  • 即使 JavaScript中沒(méi)有正式的私有對(duì)象屬性的概念,但可以使用閉包來(lái)實(shí)現(xiàn)公有方法芒粹,而通過(guò)公 有方法可以訪問(wèn)在包含作用域中定義的變量化漆。
  • 有權(quán)訪問(wèn)私有變量的公有方法叫做特權(quán)方法座云。
  • 可以使用構(gòu)造函數(shù)模式锨苏、原型模式來(lái)實(shí)現(xiàn)自定義類型的特權(quán)方法伞租,也可以使用模塊 模式、增強(qiáng) 的模塊模式來(lái)實(shí)現(xiàn)單例的特權(quán)方法裸弦。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末理疙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子砖顷,更是在濱河造成了極大的恐慌滤蝠,老刑警劉巖授嘀,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件览闰,死亡現(xiàn)場(chǎng)離奇詭異巷折,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)晴弃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門上鞠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)芍阎,“玉大人谴咸,你說(shuō)我怎么就攤上這事骗露。” “怎么了珊随?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵鲫凶,是天一觀的道長(zhǎng)衩辟。 經(jīng)常有香客問(wèn)我艺晴,道長(zhǎng),這世上最難降的妖魔是什么换吧? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任钥星,我火速辦了婚禮谦炒,結(jié)果婚禮上宁改,老公的妹妹穿的比我還像新娘魂莫。我一直安慰自己,他們只是感情好谜喊,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開(kāi)白布斗遏。 她就那樣靜靜地躺著鞋邑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪逾一。 梳的紋絲不亂的頭發(fā)上遵堵,一...
    開(kāi)封第一講書(shū)人閱讀 51,215評(píng)論 1 299
  • 那天怨规,我揣著相機(jī)與錄音汪茧,去河邊找鬼舱污。 笑死扩灯,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的珠插。 我是一名探鬼主播捻撑,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼顾患,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼个唧!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起犁河,我...
    開(kāi)封第一講書(shū)人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤桨螺,失蹤者是張志新(化名)和其女友劉穎彭谁,沒(méi)想到半個(gè)月后允扇,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年唱矛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片管闷。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡包个,死狀恐怖冤留,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情糯而,我是刑警寧澤熄驼,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布烘豹,位于F島的核電站,受9級(jí)特大地震影響吴叶,放射性物質(zhì)發(fā)生泄漏蚌卤。R本人自食惡果不足惜奥秆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一侮叮、第九天 我趴在偏房一處隱蔽的房頂上張望悼瘾。 院中可真熱鬧,春花似錦卸勺、人聲如沸曙求。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)苹享。三九已至挣菲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間椭赋,已是汗流浹背哪怔。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工认境, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留叉信,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像覆享,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子丑罪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

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

  • 本章內(nèi)容 函數(shù)表達(dá)式的特征 使用函數(shù)實(shí)現(xiàn)遞歸 使用閉包定義私有變量 第五章曾介紹過(guò),定義函數(shù)的方式有兩種:一種是函...
    Annie_d04e閱讀 258評(píng)論 0 1
  • 函數(shù)表達(dá)式是 JavaScript中的一個(gè)既強(qiáng)大又容易令人困惑的特性拧抖。第 5章曾介紹過(guò)祟峦,定義函數(shù)的 方式有兩種:一...
    Xyaleo閱讀 192評(píng)論 0 1
  • 1.定義函數(shù)的方式: ①函數(shù)聲明(其重要特征是函數(shù)聲明提升,可以把函數(shù)聲明放在調(diào)用它的語(yǔ)句后面): functio...
    張果果閱讀 222評(píng)論 0 0
  • 定義函數(shù)的兩種方式:函數(shù)聲明和函數(shù)表達(dá)式徙鱼。函數(shù)聲明: sayHi();function sayHi() { al...
    Allenevil閱讀 172評(píng)論 0 0
  • 函數(shù)變量提升:在執(zhí)行代碼之前會(huì)先讀取函數(shù)聲明宅楞。這就意味著可以把函數(shù)聲明放在調(diào)用它的語(yǔ)句后面针姿。 函數(shù)聲明: 函數(shù)表達(dá)...
    簡(jiǎn)默丶XS閱讀 599評(píng)論 0 1