澄清誤解,詳解JS立即執行函數

澄清誤解,詳解JS立即執行函數

JS只有函數作用域,沒有塊級作用域。於是,聰明的程序猿們想出來利用函數作用域模仿塊

級作用域的“錦囊妙計”,並被廣泛使用:

(function (value) {
    alert(value);
}('hello'));

(function (value) {
    alert(value);
})('hello');

! function (value) {
    alert(value);
} ('world');

+ function (value) {
    alert(value);
} ('world');

- function (value) {
    alert(value);
} ('world');

但上面千奇百怪的符號往往令初學者無比困惑。百度到的答案令人遺憾,很多都是曲解,(一個排名很靠前的博客把問題總結爲:函數聲明、函數表達式、匿名函數的區別,認爲函數表達式後面可以加括號立即調用該函數,函數聲明不可以。這根本沒有理解本質所在)

這也讓我在很長的一段時間裏對立即執行函數感到迷惘。然而其實在《javascript精粹》和《JavaScript權威指南》中說的很清楚,我寫這篇文章的目的所在,正是澄清這個問題。

首先,我們要明確的是,這些寫法的目的都是相同的,立即執行裏面的匿名函數(以達到閉包等等“不可告人的目的”)

爲了解開其中的奧祕,首先讓我們明確這些結構的組成部分——表達式

表達式

(1) 函數表達式

函數表達式有點類似於“函數直接量”,其中[]表示可選。

function [funName] () {
  // ... ...
}

函數表達式還可以賦給某個變量

var sayHello = function () { ... ... }

(2) 調用表達式

語法:

函數表達式()

例如:

sayHello('Tom')

這樣,會執行sayHello這個函數。也就是說,在函數後面加上()並傳遞相應的參數就可以執行在函數裏定義的語句。

JS的原則

立即調用的函數不需要函數名,直接調用即可,所以很多“聰明人”的代碼出現了下面的錯誤:

function (value) {
    alert(value);
}('hello')

報:Unexpected token ( 錯誤。錯誤的原因不在於聰明的程序猿們,因爲這的確符合調用表達式的語法。其實,問題的本質在於JS的一項“霸王條款”:

一個語句不能以一個函數表達式開頭,因爲官方語法假定以單詞function開頭的語句是一個function語句
(《javascript精粹》114頁)

這下答案揭曉了:上面立即調用函數句式的原因正在於此。JS引擎認爲以function開頭的是函數聲明語句,不能是函數調用語句。

下面的代碼沒有問題,因爲語句的最前面並非function:

var index = function () {
    alert('函數表達式執行了!!');
    return 666
} ();

index // =>666

解決問題

解決問題的最簡單辦法就是不讓function出現在語句的最前面。()這個“物美價廉,綠色無公害”的運算符遍成了首選,所以立即執行函數的最常見形式是:

(function (value) {
    alert(value);
}('hello'));

相對而言,!等運算符也行有副作用,而且結果往往令人大喫一驚:

var index = !function () {
    alert('匿名函數執行了!!');
    return 666
} ();

index // =>false

index變爲fasle的原因是!爲666做了強制轉換並取了反。

大師Douglas Crockford的 javascript:the good part (《javascript精粹》)真是一本javascript的絕世祕籍,令人由衷讚歎!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章