我們都知道,函數的聲明方式有這兩種
function fnA(){alert('msg');}//聲明式定義函數
var fnB = function(){alert('msg');}//函數賦值表達式定義函數
函數的調用方式通常是FunctionName()
但是,如果我們嘗試爲一個“定義函數”末尾加上()
,解析器是無法理解的。
function msg(){
alert('message');
}();//解析器是無法理解的
定義函數的調用方式應該是 msg();
那爲什麼將函數體部分用()
包裹起來就可以了呢?
原來,**使用括號包裹定義函數體,解析器將會以函數表達式的方式去調用定義函數。**也就是說,任何能將函數變成一個函數表達式的作法,都可以使解析器正確的調用定義函數。而 !
就是其中一個,而 + - || ~
都有這樣的功能。
另外,用 !
可能更多的是一個習慣問題,不同的運算符,性能是不同的。
// 這麼寫會報錯,因爲這是一個函數定義:
function() {}()
// 常見的(多了一對括號),調用匿名函數:
(function() {})()
// 但在前面加上一個布爾運算符(只多了一個感嘆號),就是表達式了,將執行後面的代碼,也就合法實現調用
!function() {}()
js中function
前面加 !
的含義
自執行匿名函數:
- 常見格式:
(function() { /* code */ })();
- 解釋:包圍函數
(function(){})
的第一對括號向腳本返回未命名的函數,隨後一對空括號立即執行返回的未命名函數,括號內爲匿名函數的參數。 - 作用:可以用它創建命名空間,只要把自己所有的代碼都寫在這個特殊的函數包裝內,那麼外部就不能訪問,除非你允許(變量前加上
window
,這樣該函數或變量就成爲全局)。各JavaScript庫的代碼也基本是這種組織形式。
總結一下,執行函數的作用主要爲 匿名 和 自動執行,代碼在被解釋時就已經在運行了。
其他寫法
(function () { /* code */ } ());
!function () { /* code */ } ();
~function () { /* code */ } ();
-function () { /* code */ } ();
+function () { /* code */ } ();
從function
前面的!
想到的
最近沒事喜歡看看,一些js庫的源碼,結果發現庫前不是加一個!
就是加+
或者一個()
,心中猜出個大概知道這個是讓函數自動執行,可是這麼多符號達到同一個目的,原理是什麼呢,下面做一下剖析:
先從IIFE
開始介紹
IIFE(Imdiately Invoked Function Expression 立即執行的函數表達式)
function(){
alert('IIFE');
}
把這個代碼放在console
中執行會報錯
因爲這個是一個匿名函數,要想讓它正常運行就必須給個函數名,然後通過函數名調用。
好了這下知道爲啥我們看到很多類庫寫的時候也是匿名函數結果不報錯了吧,就是因爲這些前面加的符號的原因。
其實在匿名函數前面加上這些符號後,就把一個函數聲明語句變成了一個函數表達式,是表達式就會在script
標籤中自動執行。
運算符
①爲什麼加上了這些運算符後就能讓一個匿名函數變成一個不會報錯的函數表達式呢?
我們自然會想到javascript的解析器到底是怎麼工作識別的呢,js解析器執行js表達式這個肯定是沒有問題的。其實無論是括號,還是感嘆號,讓整個語句合法做的事情只有一件,就是讓一個函數聲明語句變成了一個表達式。所以我們讓一個函數定義變成一個函數表達式來執行就不會報錯。
②原理
這樣是一個函數聲明
function a(){
alert('IIFE');
}
這樣是一個函數調用
a();
理解一下就是在一個聲明瞭的函數後面加上一個()
就可以調用函數了
function a(){
alert('IIFE');
}()
就這樣
但是我們按上面在console
中執行發現出錯了
因爲這樣的代碼混淆了函數聲明和函數調用,以這種方式聲明的函數 a
,就應該以 a();
的方式調用。
但是括號則不同,它將一個函數聲明轉化成了一個表達式,解析器不再以函數聲明的方式處理函數a
,而是作爲一個函數表達式處理,也因此只有在程序執行到函數a
時它才能被訪問。所以,任何消除函數聲明和函數表達式間歧義的方法,都可以被解析器正確識別。所以,賦值,邏輯,甚至是逗號,各種操作符都可以告訴解析器,這個不是函數聲明,它是個函數表達式。並且,對函數一元運算可以算的上是消除歧義最快的方式,感嘆號只是其中之一,如果不在乎返回值,這些一元運算都是有效的
!function(){alert('iifksp')}() // true
+function(){alert('iifksp')}() // NaN
-function(){alert('iifksp')}() // NaN
~function(){alert('iifksp')}() // -1
性能
針對這些一元運算符,到底用哪個好呢,測試發現()
的性能最優越,但是差別都不是特明顯,所以對於一個庫來說用幾個這樣的符號來說看不出什麼影響,所以平常用! + -
都可以,就看個人的代碼習慣,當然最好還是用()
。