成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

立即執(zhí)行函數(shù)表達(dá)式(IIFE)

Yu_Huang / 2581人閱讀

摘要:另外,如果你想跳過這里,你可以直接跳到立即調(diào)用函數(shù)表達(dá)式進(jìn)行閱讀,但是我建議你讀完整篇文章。當(dāng)圓括號(hào)包裹函數(shù)時(shí),它會(huì)默認(rèn)將函數(shù)作為表達(dá)式去解析,而不是函數(shù)聲明。

原文:Immediately-Invoked Function Expression (IIFE) by Ben Alman
原譯:立即執(zhí)行函數(shù) by Murphywuwu
改增內(nèi)容: by blanu

也許你沒有注意到,我是一個(gè)對(duì)于專業(yè)術(shù)語有一點(diǎn)強(qiáng)迫癥的人。所以,當(dāng)我多次聽到流行卻易產(chǎn)生誤解的術(shù)語「自執(zhí)行匿名函數(shù)」,我最終決定將我的想法寫進(jìn)這篇文章里。

更進(jìn)一步地說,除了提供關(guān)于該模式究竟是如何工作的全面信息,事實(shí)上我還建議了我們應(yīng)該怎樣稱呼這種模式。另外,如果你想跳過這里,你可以直接跳到立即調(diào)用函數(shù)表達(dá)式進(jìn)行閱讀,但是我建議你讀完整篇文章。

請(qǐng)理解這篇文章不是想說「我對(duì)了,你錯(cuò)了」。我發(fā)自真心地想幫助人們理解看似復(fù)雜的概念,并且我認(rèn)為使用前后一致的精確術(shù)語是有助于人們理解的最簡單的方式之一。

它是什么

在JavaScript里,每個(gè)函數(shù),當(dāng)被調(diào)用時(shí),都會(huì)創(chuàng)建一個(gè)新的執(zhí)行上下文。因?yàn)樵诤瘮?shù)里定義的變量和函數(shù)只能在函數(shù)內(nèi)部被訪問,外部無法獲??;當(dāng)調(diào)用函數(shù)時(shí),函數(shù)提供的上下文就提供了一個(gè)非常簡單的方法創(chuàng)建私有變量。

//因?yàn)檫@個(gè)函數(shù)的返回值是另一個(gè)能訪問私有變量i的函數(shù),因此返回的函數(shù)實(shí)際上被提權(quán)(privileged)了
function makeCounter() {
    //i只能從`makeConuter`內(nèi)部訪問
    var i = 0;
    return function(){
        console.log(++i);
    };   
}
//記?。篳counter`和`counter2`都有他們自己作用域中的變量 `i`
var counter = makeCounter();
counter();//1
counter();//2

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

i;//ReferenceError: i is not defined(它只存在于makeCounter里)

在許多情況下,你可能并不需要makeWhatever這樣的函數(shù)返回多次累加值,并且可以只調(diào)用一次得到一個(gè)單一的值,在其他一些情況里,你甚至不需要明確的知道返回值。

它的核心

現(xiàn)在,無論你定義一個(gè)函數(shù)像這樣function foo(){}或者var foo = function(){},調(diào)用時(shí),你都需要在后面加上一對(duì)圓括號(hào),像這樣foo()

//向下面這樣定義的函數(shù)可以通過在函數(shù)名后加一對(duì)括號(hào)進(jìn)行調(diào)用,像這樣`foo()`,因?yàn)閒oo相對(duì)于函數(shù)表達(dá)式`function(){/* code */}`只是一個(gè)引用變量

var foo = function(){/* code */}
//那這可以說明函數(shù)表達(dá)式可以通過在其后加上一對(duì)括號(hào)自己調(diào)用自己嗎?
function(){ /* code */}();//SyntaxError: Unexpected token (

正如你所看到的,這里捕獲了一個(gè)錯(cuò)誤。當(dāng)圓括號(hào)為了調(diào)用函數(shù)出現(xiàn)在函數(shù)后面時(shí),無論在全局環(huán)境或者局部環(huán)境里遇到了這樣的function關(guān)鍵字,默認(rèn)的,它會(huì)將它當(dāng)作是一個(gè)函數(shù)聲明,而不是函數(shù)表達(dá)式,如果你不明確的告訴圓括號(hào)它是一個(gè)表達(dá)式,它會(huì)將其當(dāng)作沒有名字的函數(shù)聲明并且拋出一個(gè)錯(cuò)誤,因?yàn)楹瘮?shù)聲明需要一個(gè)名字。
問題1:這里我么可以思考一個(gè)問題,我們是不是也可以像這樣直接調(diào)用函數(shù)var foo = function(){console.log(1)}(),答案是可以的。
問題2:同樣的,我們還可以思考一個(gè)問題,像這樣的函數(shù)聲明在后面加上圓括號(hào)被直接調(diào)用,又會(huì)出現(xiàn)什么情況呢?請(qǐng)看下面的解答。

題外話:函數(shù)、圓括號(hào)和錯(cuò)誤

有趣的是,如果你為一個(gè)函數(shù)指定一個(gè)名字并在它后面放一對(duì)圓括號(hào),同樣的也會(huì)拋出錯(cuò)誤,但這次是因?yàn)榱硗庖粋€(gè)原因。當(dāng)圓括號(hào)放在一個(gè)函數(shù)表達(dá)式后面指明了這是一個(gè)被調(diào)用的函數(shù),而圓括號(hào)放在一個(gè)聲明后面便意味著完全的和前面的函數(shù)聲明分開了,此時(shí)圓括號(hào)只是一個(gè)簡單的代表一個(gè)括號(hào)(用來控制運(yùn)算優(yōu)先的括號(hào))。

//然而函數(shù)聲明語法上是無效的,它仍然是一個(gè)聲明,緊跟著的圓括號(hào)是無效的,因?yàn)閳A括號(hào)里需要包含表達(dá)式
function foo(){ /* code */ }();//SyntaxError: Unexpected token
//現(xiàn)在,你把一個(gè)表達(dá)式放在圓括號(hào)里,沒有拋出錯(cuò)誤...但是函數(shù)也并沒有執(zhí)行,因?yàn)椋?function foo(){/* code */}(1)
//它等同于如下,一個(gè)函數(shù)聲明跟著一個(gè)完全沒有關(guān)系的表達(dá)式:
function foo(){/* code */}
(1);

關(guān)于這個(gè)細(xì)節(jié),你可以閱讀Dmitry A. Soshnikov的文章:ECMA-262-3 in detail. Chapter 5. Functions (中文版本)

立即執(zhí)行函數(shù)表達(dá)式(IIFE)

幸運(yùn)的是,修正語法錯(cuò)誤很簡單。最流行的也最被接受的方法是將函數(shù)聲明包裹在圓括號(hào)里來告訴語法分析器去表達(dá)一個(gè)函數(shù)表達(dá)式,因?yàn)樵贘avaScript里,圓括號(hào)不能包含聲明。因?yàn)檫@點(diǎn),當(dāng)圓括號(hào)為了包裹函數(shù)碰上了 function關(guān)鍵詞,它便知道將它作為一個(gè)函數(shù)表達(dá)式去解析而不是函數(shù)聲明。注意理解這里的圓括號(hào)和上面的圓括號(hào)遇到函數(shù)時(shí)的表現(xiàn)是不一樣的,也就是說。

當(dāng)圓括號(hào)出現(xiàn)在匿名函數(shù)的末尾想要調(diào)用函數(shù)時(shí),它會(huì)默認(rèn)將函數(shù)當(dāng)成是函數(shù)聲明。

當(dāng)圓括號(hào)包裹函數(shù)時(shí),它會(huì)默認(rèn)將函數(shù)作為表達(dá)式去解析,而不是函數(shù)聲明。

//這兩種模式都可以被用來立即調(diào)用一個(gè)函數(shù)表達(dá)式,利用函數(shù)的執(zhí)行來創(chuàng)造私有變量
(function(){/* code */}());//Crockford recommends this one
(function(){/* code */})();//But this one works just as well

// 因?yàn)槔ㄌ?hào)的作用就是為了消除函數(shù)表達(dá)式和函數(shù)聲明之間的差異
// 如果解釋器能預(yù)料到這是一個(gè)表達(dá)式,括號(hào)可以被省略
// 不過請(qǐng)參見下面的「重要筆記」
var i = function(){return 10;}();
true && function(){/*code*/}();
0,function(){}();

//如果你并不關(guān)心返回值,或者讓你的代碼盡可能的易讀,你可以通過在你的函數(shù)前面帶上一個(gè)一元操作符來存儲(chǔ)字節(jié)
!function(){/* code */}();
~function(){/* code */}();
-function(){/* code */}();
+function(){/* code */}();

// 這里是另外一種方法
// 我(原文作者)不清楚new方法是否會(huì)影響性能
// 但它卻是奏效,參見http://twitter.com/kuvos/status/18209252090847232

new function(){ /* code */ }
new function(){ /* code */ }() // 只有當(dāng)傳入?yún)?shù)時(shí)才需要加括號(hào)
關(guān)于括號(hào)的重要筆記

在一些情況下,當(dāng)額外的帶著歧義的括號(hào)圍繞在函數(shù)表達(dá)式周圍是沒有必要的(因?yàn)檫@時(shí)候的括號(hào)已經(jīng)將其作為一個(gè)表達(dá)式去表達(dá)),但當(dāng)括號(hào)用于調(diào)用函數(shù)表達(dá)式時(shí),這仍然是一個(gè)好主意。

這樣的括號(hào)指明函數(shù)表達(dá)式將會(huì)被立即調(diào)用,并且變量將會(huì)儲(chǔ)存函數(shù)的結(jié)果,而不是函數(shù)本身。當(dāng)這是一個(gè)非常長的函數(shù)表達(dá)式時(shí),這可以節(jié)約其他人閱讀你代碼的時(shí)間,不用滾到頁面底部去看這個(gè)函數(shù)是否被調(diào)用。

作為規(guī)則,當(dāng)你書寫清楚明晰的代碼時(shí),有必要阻止JavaScript拋出錯(cuò)誤的,同樣也有必要阻止其他開發(fā)者對(duì)你拋出錯(cuò)誤WTFError!

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

就像當(dāng)函數(shù)通過他們的名字被調(diào)用時(shí),參數(shù)會(huì)被傳遞,而當(dāng)函數(shù)表達(dá)式被立即調(diào)用時(shí),參數(shù)也會(huì)被傳遞。一個(gè)立即調(diào)用的函數(shù)表達(dá)式可以用來鎖定值并且有效的保存此時(shí)的狀態(tài),因?yàn)槿魏味x在一個(gè)函數(shù)內(nèi)的函數(shù)都可以使用外面函數(shù)傳遞進(jìn)來的參數(shù)和變量(這種關(guān)系被叫做閉包)。

關(guān)于閉包的更多信息,參見 Closures explained with JavaScript

//它的運(yùn)行原理可能并不像你想的那樣,因?yàn)閌i`的值從來沒有被鎖定。相反的,每個(gè)鏈接,當(dāng)被點(diǎn)擊時(shí)(循環(huán)已經(jīng)被很好的執(zhí)行完畢),因此會(huì)彈出所有元素的總數(shù),因?yàn)檫@是`i`此時(shí)的真實(shí)值。
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);
}
//而像下面這樣改寫,便可以了,因?yàn)樵贗IFE里,`i`值被鎖定在了`lockedInIndex`里。在循環(huán)結(jié)束執(zhí)行時(shí),盡管`i`值的數(shù)值是所有元素的總和,但每一次函數(shù)表達(dá)式被調(diào)用時(shí),IIFE里的`lockedInIndex`值都是`i`傳給它的值,所以當(dāng)鏈接被點(diǎn)擊時(shí),正確的值被彈出。
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);
}
//你同樣可以像下面這樣使用IIFE,僅僅只用括號(hào)包裹點(diǎn)擊處理函數(shù),并不包含整個(gè)`addEventListener`。無論用哪種方式,這兩個(gè)例子都可以用IIFE將值鎖定,不過我發(fā)現(xiàn)前面一個(gè)例子更可讀
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);
    }

記住,在這最后兩個(gè)例子里,lockedInIndex可以沒有任何問題的訪問i,但是作為函數(shù)的參數(shù)使用一個(gè)不同的命名標(biāo)識(shí)符可以使概念更加容易的被解釋。

立即執(zhí)行函數(shù)一個(gè)最顯著的優(yōu)勢是就算它沒有命名或者說是匿名,函數(shù)表達(dá)式也可以在沒有使用標(biāo)識(shí)符的情況下被立即調(diào)用,一個(gè)閉包也可以在沒有當(dāng)前變量污染的情況下被使用。

「自執(zhí)行匿名函數(shù)(Self-executing anonymous function)」有什么問題呢?

你看到它已經(jīng)被提到好幾次了,但它仍未被清楚地解釋,我提議將術(shù)語改成"Immediately-Invoked Function Expression",或者,IIFE,如果你喜歡縮寫的話(發(fā)音類似“iffy”)。

什么是Immediately-Invoked Function Expression呢?顧名思義,它就是一個(gè)被立即調(diào)用的函數(shù)表達(dá)式。

我想JavaScript社區(qū)的成員應(yīng)該可以在他們的文章里或者陳述里接受術(shù)語Immediately-Invoked Function ExpressionIIFE,因?yàn)槲腋杏X這樣更容易讓這個(gè)概念被理解,并且術(shù)語"self-executing anonymous function"真的也不夠精確。

//下面是個(gè)自執(zhí)行函數(shù),遞歸的調(diào)用自己本身
function foo(){foo();};
//這是一個(gè)自執(zhí)行匿名函數(shù)。因?yàn)樗鼪]有標(biāo)識(shí)符,它必須是使用`arguments.callee`屬性來調(diào)用它自己
var foo = function(){arguments.callee();};
//這也許算是一個(gè)自執(zhí)行匿名函數(shù),但是僅僅當(dāng)`foo`標(biāo)識(shí)符作為它的引用時(shí),如果你將它換成用`foo`來調(diào)用同樣可行
var foo = function(){foo();};
//有些人像這樣叫"self-executing anonymous function"下面的函數(shù),即使它不是自執(zhí)行的,因?yàn)樗]有調(diào)用它自己。然后,它只是被立即調(diào)用了而已。
(function(){ /*code*/ }());
//為函數(shù)表達(dá)式增加標(biāo)識(shí)符(也就是說創(chuàng)造一個(gè)命名函數(shù))對(duì)我們的調(diào)試會(huì)有很大幫助。一旦命名,函數(shù)將不再匿名。
(function foo(){/* code */}());
//IIFEs同樣也可以自執(zhí)行,盡管,也許他不是最有用的模式
(function(){arguments.callee();}())
(function foo(){foo();}())
// 另外,下面這個(gè)表達(dá)式竟會(huì)在黑莓5上拋出錯(cuò)誤,在一個(gè)被命名的函數(shù)中,該函數(shù)名是undefined。很奇妙吧…
(function foo(){ foo(); }());

希望上面的例子可以讓你更加清楚的知道術(shù)語"self-executing"是有一些誤導(dǎo)的,因?yàn)樗⒉皇菆?zhí)行自己的函數(shù),盡管函數(shù)已經(jīng)被執(zhí)行。同樣的,匿名函數(shù)也沒用必要特別指出,因?yàn)椋?strong>Immediately Invoked Function Expression,既可以是命名函數(shù)也可以匿名函數(shù)。

有趣的是:因?yàn)閍rguments.callee在ECMAScript 5 strict mode中被deprecated了,所以在ES5的strict mode中實(shí)際上不可能創(chuàng)建一個(gè)self-executing anonymous function

最后:模塊模式

當(dāng)我調(diào)用函數(shù)表達(dá)式時(shí),如果我不至少一次的提醒我自己關(guān)于模塊模式,我便很可能會(huì)忽略它。如果你并不熟悉JavaScript里的模塊模式,它和我第一個(gè)例子很像,但是返回值用對(duì)象代替了函數(shù)。

var counter = (function(){
    var i = 0;
    return {
        get: function(){
            return i;
        },
        set: function(val){
            i = val;
        },
        increment: function(){
            return ++i;
        }
    }
    }());
    counter.get();//0
    counter.set(3);
    counter.increment();//4
    counter.increment();//5

    conuter.i;//undefined (`i` is not a property of the returned object)
    i;//ReferenceError: i is not defined (it only exists inside the closure)

模塊模式方法不僅相當(dāng)?shù)膮柡Χ液唵?。非常少的代碼,你可以有效的利用與方法和屬性相關(guān)的命名,在一個(gè)對(duì)象里,組織全部的模塊代碼即最小化了全局變量的污染也創(chuàng)造了私人變量。

延伸閱讀

希望這篇文章可以為你答疑解惑。當(dāng)然,如果你產(chǎn)生了更多疑惑,你可以閱讀下面這些關(guān)于函數(shù)和模塊模式的文章。

ECMA-262-3 in detail. Chapter 5. Functions. - Dmitry A. Soshnikov

Functions and function scope - Mozilla Developer Network

Named function expressions - Juriy “kangax” Zaytsev

JavaScript Module Pattern: In-Depth - Ben Cherry

Closures explained with JavaScript - Nick Morgan

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/81004.html

相關(guān)文章

  • JavaScript-立即調(diào)用函數(shù)達(dá)式IIFE

    摘要:將匿名函數(shù)賦予一個(gè)變量,叫函數(shù)表達(dá)式,這是最常見的函數(shù)表達(dá)式語法形式。組成這是一個(gè)被稱為自執(zhí)行匿名函數(shù)的設(shè)計(jì)模式,主要包含兩部分。 一、函數(shù)聲明&函數(shù)表達(dá)式 1.1 函數(shù)聲明 (函數(shù)語句) showImg(https://segmentfault.com/img/bVbbqvT?w=278&h=166); (1)使用 function 關(guān)鍵字聲明一個(gè)函數(shù),再指定一個(gè)函數(shù)名,叫函數(shù)聲明。...

    XUI 評(píng)論0 收藏0
  • IIFE 疑惑解析

    摘要:問題解答定義了一個(gè)名為的立即執(zhí)行函數(shù)表達(dá)式我們來看這一段代碼,這里面包含著一個(gè)名為的具名函數(shù)表達(dá)式。 IIFE 是什么呢? IIFE 是 Immediately-Invoked Function Expression 的英文縮寫,也就是立即執(zhí)行函數(shù)表達(dá)式。 下面是一個(gè) IIFE 代碼實(shí)例: (function(){ console.log( Hello! ); ...

    Warren 評(píng)論0 收藏0
  • Javascript知識(shí)點(diǎn):IIFE - 立即調(diào)用函數(shù)

    摘要:所以那些匿名函數(shù)附近使用括號(hào)或一些一元運(yùn)算符的慣用法,就是來引導(dǎo)解析器,指明運(yùn)算符附近是一個(gè)表達(dá)式。 Immediately-invoked Function Expression(IIFE,立即調(diào)用函數(shù)),簡單的理解就是定義完成函數(shù)之后立即執(zhí)行。因此有時(shí)候也會(huì)被稱為自執(zhí)行的匿名函數(shù)(self-executing anonymous function)。 IIFE的叫法最早見于Ben...

    goji 評(píng)論0 收藏0
  • [譯] JavaScript:立即執(zhí)行函數(shù)達(dá)式IIFE

    摘要:而且,如果你想跳過這里,你可以直接跳到立即調(diào)用函數(shù)表達(dá)式進(jìn)行閱讀,但是我建議你讀完整篇文章。當(dāng)圓括號(hào)包裹函數(shù)時(shí),它會(huì)默認(rèn)將函數(shù)作為表達(dá)式去解析,而不是函數(shù)聲明。什么是呢它使一個(gè)被立即調(diào)用的函數(shù)表達(dá)式。一旦命名,函數(shù)將不再匿名。 原文:http://benalman.com/news/2010/11/immediately-invoked-function-expression/#iif...

    546669204 評(píng)論0 收藏0
  • 7種JS-IIFE立即執(zhí)行函數(shù))寫法

    IIFE(Immediately Invoked Function Expressions) 叫做立即執(zhí)行表達(dá)式,顧名思義,該表達(dá)式一被創(chuàng)建就立即執(zhí)行。 1.對(duì)返回結(jié)果不進(jìn)行處理 (function(形參){ 函數(shù)體內(nèi)容 })(實(shí)參); 2.對(duì)返回結(jié)果不進(jìn)行處理 (function(形參){ 函數(shù)體內(nèi)容 }(實(shí)參)); 3.返回的是一個(gè)布爾值,然后進(jìn)行取反 !function(形...

    xorpay 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<