首頁 >web前端 >js教程 >JavaScript匿名、具名函數與立即執行函數IIFE詳解

JavaScript匿名、具名函數與立即執行函數IIFE詳解

黄舟
黄舟原創
2017-02-28 15:06:452730瀏覽

JavaScript中的函數作用域的存在就是為了把變數和函數「隱藏」起來 

#符合我們的最小特權原則
同時它的另一個好處是可以避免同名標識符的衝突
今天主要來談談這個立即執行函數
在此之前的複習一下匿名函數與具名函數

匿名函數與具名函數

#無論是匿名還是具名,都是針對函數表達式來說的
函數宣告那就必須得有名字了,否則會報錯的

function foo(){ //函数声明
    //...}

這裡我解釋一個彩蛋,可能大家看我寫文章的時候範例程式碼總是喜歡用一些
 fn、func、demo、foo、bar、foobar之類的字
 fn、func就是function「函數」的縮寫
 demo就是單字demonstration「範例」的縮寫
 foo、bar、foobar我們常常能夠在技術書籍還有電腦文獻中看到
 foo是fu的變體,fuck-up縮寫,意思是「一團糟」
 bar是“beyond all recognition”,意思是“超越認知”,通俗說“識別不了,一塌糊塗”
就是一些佔位詞,相當於我們的小明小紅、甲乙丙丁、張三李四…

函數表達式是可以有名字,也可以沒有名字的

var foo = function(){...};
console.log(foo.name); //foo
var bar = function foobar(){...};//不要这么写
console.log(bar.name); //foobar

可以看到第二種寫法只是單純地改變了函數的name屬性
除此之外百無一用,我們不要這麼寫

除此之外我們最常見的用法就是函數表達式作為回呼參數了
比如說在定時器中

setTimeout(function(){
    //...},1000);

這裡我寫了匿名函數表達式
它用起來簡單粗暴
但有幾個缺點

  • 追蹤堆疊中沒函數名,調試困難

  • 如果需要引用自身,只能用arguments.callee(ES5嚴格模式停用)

  • 降低了函數可讀性、可理解性

我們在解除事件綁定時,也需要函數名稱(如果不是用onclick等等直接綁定的話)
所以
給函數表達式一個名字是一個最佳實踐,是一個好習慣

setTimeout(function timerHandler(){
    //...},1000);

立即執行函數常用用法

立即執行函數,我習慣這麼叫
也有叫自執行函數、自動執行函數什麼的
說的更標準一些
立即執行函數表達式
注意我的用詞,是函數表達式

它不是什麼語法
而是大家用著用著發現還能這麼用
於是就流傳開來,直到現在
顧名思義,就是執行流運行到這個函數就立刻執行了
社區給它規定了術語:IIFE(Immediately Invoked Function Expression)

我們常用的用法是:

(function(){
    //...}());
(function(){
    //...})();

我們用IIFE的時候一般都是使用一個匿名函數來表達式
當然你加上名字也不錯,同時擁有了具名函數表達式的優點
兩種用法完全等價,使用哪種由你決定
但我更願意把括號寫在外面,感覺看著比較舒服
而且看到很多大神也跟我一樣

立即執行函數進階用法

稍微進階的用法就是在立即執行函數中傳遞參數
比如說我們常見的傳遞window

var a = '全局';
(function IIFE(global){    var a = '局部';    console.log(a);// "局部"
    console.log(global.a);// "全局"})(window);

傳window有什麼好處呢?
從我們作用域的角度分析
(可以看看我寫的作用域->傳送門)
全域物件的參考快取到了局部環境
這樣我們可以更快的存取到全域對象,不用再跳到頂級作用域查找
這點小優化不是重點
重點的壓縮程式碼的時候可以進行最佳化
也許上面的程式碼壓縮工具可以壓縮成這樣

var a = '全局';
(function IIFE(g){    var a = '局部';    console.log(a);// "局部"
    console.log(g.a);// "全局"})(window);

當程式碼非常大的時候,可不要小看這優化

立即執行函數變化用法

#IIFE還有一種變式,你一定看過

(function IIFE(demo){
    demo(window);})(function demo(global){
    //...
});

這種把要運行的函數放進參數的模式看起來很麻煩
但卻被廣泛使用,jQuery等框架整體架構也和這個很像
如果看習慣了,就會感覺它比我們傳統的使用方法更容易理解

整體的函數表達式定義在IIFE第二部分,作為參數傳遞進IIFE第一部分
然後調用了這個函數,並且把window作為參數傳入

立即執行函數的理解

立即執行函數為什麼能夠自動執行?

(function(){
    //...})();

為什麼function(){}()這樣寫不可以
因為這是函數聲明,函數宣告是不可以執行的
只有表達式才能夠執行

var demo = function(){
    console.log(1);// 1
    return 123;
}();
console.log(demo);// 123

這就是一個表達式,所以可以被執行
demo得到的是函數的回傳值
既然表達式可以被執行,那麼以下方式都可以被立即執行

+function(){
    console.log(1);// 1}();
-function(){
    console.log(2);// 2}();
!function(){
    console.log(3);// 3}()
,function(){
    console.log(4);// 4}();

可以看出甚至是逗號,都可以它們變成函數表達式,然後立刻執行
不過我們千萬不要這麼寫
如果函數有回傳值的話,可能會產生意想不到的副作用
且可讀性也不好

總結

  • 函數宣告必須有名字

  • 具名函數表達式是一個最佳實踐

  • 立即執行函數表達式用法:(function(){}()); (function(){})();

  • #立即執行函數傳遞window參數最佳化在作用域鏈中尋找window速度,有利於壓縮程式碼

  • 只有表達式才能夠被執行

 以上就是JavaScript匿名、具名函數與立即執行函數IIFE詳解 的內容,更相關內容請關注PHP中文網(www.php.cn)!


#
陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn