国产chinesehdxxxx野外,国产av无码专区亚洲av琪琪,播放男人添女人下边视频,成人国产精品一区二区免费看,chinese丰满人妻videos

JavaScript 立即調(diào)用的函數(shù)表達(dá)式

2018-07-28 16:34 更新

前言

大家學(xué)JavaScript的時(shí)候,經(jīng)常遇到自執(zhí)行匿名函數(shù)的代碼,今天我們主要就來(lái)想想說(shuō)一下自執(zhí)行。

在詳細(xì)了解這個(gè)之前,我們來(lái)談了解一下“自執(zhí)行”這個(gè)叫法,本文對(duì)這個(gè)功能的叫法也不一定完全對(duì),主要是看個(gè)人如何理解,因?yàn)橛械娜苏f(shuō)立即調(diào)用,有的人說(shuō)自動(dòng)執(zhí)行,所以你完全可以按照你自己的理解來(lái)取一個(gè)名字,不過(guò)我聽很多人都叫它為“自執(zhí)行”,但作者后面說(shuō)了很多,來(lái)說(shuō)服大家稱呼為“立即調(diào)用的函數(shù)表達(dá)式”。

本文英文原文地址:http://benalman.com/news/2010/11/immediately-invoked-function-expression/

什么是自執(zhí)行?

在JavaScript里,任何function在執(zhí)行的時(shí)候都會(huì)創(chuàng)建一個(gè)執(zhí)行上下文,因?yàn)闉閒unction聲明的變量和function有可能只在該function內(nèi)部,這個(gè)上下文,在調(diào)用function的時(shí)候,提供了一種簡(jiǎn)單的方式來(lái)創(chuàng)建自由變量或私有子function。

// 由于該function里返回了另外一個(gè)function,其中這個(gè)function可以訪問(wèn)自由變量i
// 所有說(shuō),這個(gè)內(nèi)部的function實(shí)際上是有權(quán)限可以調(diào)用內(nèi)部的對(duì)象。

function makeCounter() {
    // 只能在makeCounter內(nèi)部訪問(wèn)i
    var i = 0;

    return function () {
        console.log(++i);
    };
}

// 注意,counter和counter2是不同的實(shí)例,分別有自己范圍內(nèi)的i。

var counter = makeCounter();
counter(); // logs: 1
counter(); // logs: 2

var counter2 = makeCounter();
counter2(); // logs: 1
counter2(); // logs: 2

alert(i); // 引用錯(cuò)誤:i沒(méi)有defind(因?yàn)閕是存在于makeCounter內(nèi)部)。

很多情況下,我們不需要makeCounter多個(gè)實(shí)例,甚至某些case下,我們也不需要顯示的返回值,OK,往下看。

 問(wèn)題的核心

當(dāng)你聲明類似function foo(){}或var foo = function(){}函數(shù)的時(shí)候,通過(guò)在后面加個(gè)括弧就可以實(shí)現(xiàn)自執(zhí)行,例如foo(),看代碼:

// 因?yàn)橄胂旅娴谝粋€(gè)聲明的function可以在后面加一個(gè)括弧()就可以自己執(zhí)行了,比如foo(),
// 因?yàn)閒oo僅僅是function() { / code / }這個(gè)表達(dá)式的一個(gè)引用

var foo = function(){ / code / }

// ...是不是意味著后面加個(gè)括弧都可以自動(dòng)執(zhí)行?

function(){ / code / }(); // SyntaxError: Unexpected token (
//

上述代碼,如果甚至運(yùn)行,第2個(gè)代碼會(huì)出錯(cuò),因?yàn)樵诮馕銎鹘馕鋈值膄unction或者function內(nèi)部function關(guān)鍵字的時(shí)候,默認(rèn)是認(rèn)為function聲明,而不是function表達(dá)式,如果你不顯示告訴編譯器,它默認(rèn)會(huì)聲明成一個(gè)缺少名字的function,并且拋出一個(gè)語(yǔ)法錯(cuò)誤信息,因?yàn)閒unction聲明需要一個(gè)名字。

旁白:函數(shù)(function),括弧(paren),語(yǔ)法錯(cuò)誤(SyntaxError)

有趣的是,即便你為上面那個(gè)錯(cuò)誤的代碼加上一個(gè)名字,他也會(huì)提示語(yǔ)法錯(cuò)誤,只不過(guò)和上面的原因不一樣。在一個(gè)表達(dá)式后面加上括號(hào)(),該表達(dá)式會(huì)立即執(zhí)行,但是在一個(gè)語(yǔ)句后面加上括號(hào)(),是完全不一樣的意思,他的只是分組操作符。

// 下面這個(gè)function在語(yǔ)法上是沒(méi)問(wèn)題的,但是依然只是一個(gè)語(yǔ)句
// 加上括號(hào)()以后依然會(huì)報(bào)錯(cuò),因?yàn)榉纸M操作符需要包含表達(dá)式

function foo(){ / code / }(); // SyntaxError: Unexpected token )

// 但是如果你在括弧()里傳入一個(gè)表達(dá)式,將不會(huì)有異常拋出
// 但是foo函數(shù)依然不會(huì)執(zhí)行
function foo(){ / code / }( 1 );

// 因?yàn)樗耆葍r(jià)于下面這個(gè)代碼,一個(gè)function聲明后面,又聲明了一個(gè)毫無(wú)關(guān)系的表達(dá)式: 
function foo(){ / code / }

( 1 );

你可以訪問(wèn)ECMA-262-3 in detail. Chapter 5. Functions 獲取進(jìn)一步的信息。

自執(zhí)行函數(shù)表達(dá)式

要解決上述問(wèn)題,非常簡(jiǎn)單,我們只需要用大括弧將代碼的代碼全部括住就行了,因?yàn)镴avaScript里括弧()里面不能包含語(yǔ)句,所以在這一點(diǎn)上,解析器在解析function關(guān)鍵字的時(shí)候,會(huì)將相應(yīng)的代碼解析成function表達(dá)式,而不是function聲明。

// 下面2個(gè)括弧()都會(huì)立即執(zhí)行

(function () { / code / } ()); // 推薦使用這個(gè)
(function () { / code / })(); // 但是這個(gè)也是可以用的

// 由于括弧()和JS的&&,異或,逗號(hào)等操作符是在函數(shù)表達(dá)式和函數(shù)聲明上消除歧義的
// 所以一旦解析器知道其中一個(gè)已經(jīng)是表達(dá)式了,其它的也都默認(rèn)為表達(dá)式了
// 不過(guò),請(qǐng)注意下一章節(jié)的內(nèi)容解釋

var i = function () { return 10; } ();
true && function () { / code / } ();
0, function () { / code / } ();

// 如果你不在意返回值,或者不怕難以閱讀
// 你甚至可以在function前面加一元操作符號(hào)

!function () { / code / } ();
~function () { / code / } ();
-function () { / code / } ();
+function () { / code / } ();

// 還有一個(gè)情況,使用new關(guān)鍵字,也可以用,但我不確定它的效率
// http://twitter.com/kuvos/status/18209252090847232

new function () { / code / }
new function () { / code / } () // 如果需要傳遞參數(shù),只需要加上括弧()

上面所說(shuō)的括弧是消除歧義的,其實(shí)壓根就沒(méi)必要,因?yàn)槔ɑ”緛?lái)內(nèi)部本來(lái)期望的就是函數(shù)表達(dá)式,但是我們依然用它,主要是為了方便開發(fā)人員閱讀,當(dāng)你讓這些已經(jīng)自動(dòng)執(zhí)行的表達(dá)式賦值給一個(gè)變量的時(shí)候,我們看到開頭有括弧(,很快就能明白,而不需要將代碼拉到最后看看到底有沒(méi)有加括弧。

用閉包保存狀態(tài)

和普通function執(zhí)行的時(shí)候傳參數(shù)一樣,自執(zhí)行的函數(shù)表達(dá)式也可以這么傳參,因?yàn)殚]包直接可以引用傳入的這些參數(shù),利用這些被lock住的傳入?yún)?shù),自執(zhí)行函數(shù)表達(dá)式可以有效地保存狀態(tài)。

// 這個(gè)代碼是錯(cuò)誤的,因?yàn)樽兞縤從來(lái)就沒(méi)背locked住
// 相反,當(dāng)循環(huán)執(zhí)行以后,我們?cè)邳c(diǎn)擊的時(shí)候i才獲得數(shù)值
// 因?yàn)檫@個(gè)時(shí)候i操真正獲得值
// 所以說(shuō)無(wú)論點(diǎn)擊那個(gè)連接,最終顯示的都是I am link #10(如果有10個(gè)a元素的話)

var elems = document.getElementsByTagName('a');

for (var i = 0; i < elems.length; i++) {

    elems[i].addEventListener('click', function (e) {
        e.preventDefault();
        alert('I am link #' + i);
    }, 'false');

}

// 這個(gè)是可以用的,因?yàn)樗谧詧?zhí)行函數(shù)表達(dá)式閉包內(nèi)部
// i的值作為locked的索引存在,在循環(huán)執(zhí)行結(jié)束以后,盡管最后i的值變成了a元素總數(shù)(例如10)
// 但閉包內(nèi)部的lockedInIndex值是沒(méi)有改變,因?yàn)樗呀?jīng)執(zhí)行完畢了
// 所以當(dāng)點(diǎn)擊連接的時(shí)候,結(jié)果是正確的

var elems = document.getElementsByTagName('a');

for (var i = 0; i < elems.length; i++) {

    (function (lockedInIndex) {

        elems[i].addEventListener('click', function (e) {
            e.preventDefault();
            alert('I am link #' + lockedInIndex);
        }, 'false');

    })(i);

}

// 你也可以像下面這樣應(yīng)用,在處理函數(shù)那里使用自執(zhí)行函數(shù)表達(dá)式
// 而不是在addEventListener外部
// 但是相對(duì)來(lái)說(shuō),上面的代碼更具可讀性

var elems = document.getElementsByTagName('a');

for (var i = 0; i < elems.length; i++) {

    elems[i].addEventListener('click', (function (lockedInIndex) {
        return function (e) {
            e.preventDefault();
            alert('I am link #' + lockedInIndex);
        };
    })(i), 'false');

}

其實(shí),上面2個(gè)例子里的lockedInIndex變量,也可以換成i,因?yàn)楹屯饷娴膇不在一個(gè)作用于,所以不會(huì)出現(xiàn)問(wèn)題,這也是匿名函數(shù)+閉包的威力。

自執(zhí)行匿名函數(shù)和立即執(zhí)行的函數(shù)表達(dá)式區(qū)別

在這篇帖子里,我們一直叫自執(zhí)行函數(shù),確切的說(shuō)是自執(zhí)行匿名函數(shù)(Self-executing anonymous function),但英文原文作者一直倡議使用立即調(diào)用的函數(shù)表達(dá)式(Immediately-Invoked Function Expression)這一名稱,作者又舉了一堆例子來(lái)解釋,好吧,我們來(lái)看看:

// 這是一個(gè)自執(zhí)行的函數(shù),函數(shù)內(nèi)部執(zhí)行自身,遞歸
function foo() { foo(); }

// 這是一個(gè)自執(zhí)行的匿名函數(shù),因?yàn)闆](méi)有標(biāo)示名稱
// 必須使用arguments.callee屬性來(lái)執(zhí)行自己
var foo = function () { arguments.callee(); };

// 這可能也是一個(gè)自執(zhí)行的匿名函數(shù),僅僅是foo標(biāo)示名稱引用它自身
// 如果你將foo改變成其它的,你將得到一個(gè)used-to-self-execute匿名函數(shù)
var foo = function () { foo(); };

// 有些人叫這個(gè)是自執(zhí)行的匿名函數(shù)(即便它不是),因?yàn)樗鼪](méi)有調(diào)用自身,它只是立即執(zhí)行而已。
(function () { / code / } ());

// 為函數(shù)表達(dá)式添加一個(gè)標(biāo)示名稱,可以方便Debug
// 但一定命名了,這個(gè)函數(shù)就不再是匿名的了
(function foo() { / code / } ());

// 立即調(diào)用的函數(shù)表達(dá)式(IIFE)也可以自執(zhí)行,不過(guò)可能不常用罷了
(function () { arguments.callee(); } ());
(function foo() { foo(); } ());

// 另外,下面的代碼在黑莓5里執(zhí)行會(huì)出錯(cuò),因?yàn)樵谝粋€(gè)命名的函數(shù)表達(dá)式里,他的名稱是undefined
// 呵呵,奇怪
(function foo() { foo(); } ());

希望這里的一些例子,可以讓大家明白,什么叫自執(zhí)行,什么叫立即調(diào)用。

注:arguments.callee在ECMAScript 5 strict mode里被廢棄了,所以在這個(gè)模式下,其實(shí)是不能用的。

最后的旁白:Module模式

在講到這個(gè)立即調(diào)用的函數(shù)表達(dá)式的時(shí)候,我又想起來(lái)了Module模式,如果你還不熟悉這個(gè)模式,我們先來(lái)看看代碼:

// 創(chuàng)建一個(gè)立即調(diào)用的匿名函數(shù)表達(dá)式
// return一個(gè)變量,其中這個(gè)變量里包含你要暴露的東西
// 返回的這個(gè)變量將賦值給counter,而不是外面聲明的function自身

var counter = (function () {
    var i = 0;

    return {
        get: function () {
            return i;
        },
        set: function (val) {
            i = val;
        },
        increment: function () {
            return ++i;
        }
    };
} ());

// counter是一個(gè)帶有多個(gè)屬性的對(duì)象,上面的代碼對(duì)于屬性的體現(xiàn)其實(shí)是方法

counter.get(); // 0
counter.set(3);
counter.increment(); // 4
counter.increment(); // 5

counter.i; // undefined 因?yàn)閕不是返回對(duì)象的屬性
i; // 引用錯(cuò)誤: i 沒(méi)有定義(因?yàn)閕只存在于閉包)

關(guān)于更多Module模式的介紹,請(qǐng)?jiān)L問(wèn)我的上一篇帖子:深入理解JavaScript系列(2):全面解析Module模式 。

更多閱讀

希望上面的一些例子,能讓你對(duì)立即調(diào)用的函數(shù)表達(dá)(也就是我們所說(shuō)的自執(zhí)行函數(shù))有所了解,如果你想了解更多關(guān)于function和Module模式的信息,請(qǐng)繼續(xù)訪問(wèn)下面列出的網(wǎng)站:

  1. ECMA-262-3 in detail. Chapter 5. Functions. - Dmitry A. Soshnikov
  2. Functions and function scope - Mozilla Developer Network
  3. Named function expressions - Juriy “kangax” Zaytsev
  4. 全面解析Module模式- Ben Cherry(大叔翻譯整理)
  5. Closures explained with JavaScript - Nick Morgan


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)