js里function前面加!

轉(zhuǎn)載自

我們都知道,函數(shù)的聲明方式有這兩種

function fnA(){alert('msg');}//聲明式定義函數(shù)
var fnB = function(){alert('msg');}//函數(shù)賦值表達(dá)式定義函數(shù)

函數(shù)的調(diào)用方式通常是FunctionName()
但是,如果我們嘗試為一個(gè)“定義函數(shù)”末尾加上(),解析器是無(wú)法理解的。

function msg(){
  alert('message');
}();//解析器是無(wú)法理解的

定義函數(shù)的調(diào)用方式應(yīng)該是 msg(); 那為什么將函數(shù)體部分用()包裹起來(lái)就可以了呢?
原來(lái),使用括號(hào)包裹定義函數(shù)體,解析器將會(huì)以函數(shù)表達(dá)式的方式去調(diào)用定義函數(shù)。也就是說(shuō),任何能將函數(shù)變成一個(gè)函數(shù)表達(dá)式的作法,都可以使解析器正確的調(diào)用定義函數(shù)。而 ! 就是其中一個(gè),而 + - || ~ 都有這樣的功能。

另外,用 ! 可能更多的是一個(gè)習(xí)慣問(wèn)題,不同的運(yùn)算符,性能是不同的。

// 這么寫會(huì)報(bào)錯(cuò),因?yàn)檫@是一個(gè)函數(shù)定義:
function() {}()

// 常見(jiàn)的(多了一對(duì)括號(hào)),調(diào)用匿名函數(shù):
(function() {})()

// 但在前面加上一個(gè)布爾運(yùn)算符(只多了一個(gè)感嘆號(hào)),就是表達(dá)式了,將執(zhí)行后面的代碼,也就合法實(shí)現(xiàn)調(diào)用
!function() {}()
js中function前面加 ! 的含義

自執(zhí)行匿名函數(shù):

  • 常見(jiàn)格式:(function() { /* code */ })();
  • 解釋:包圍函數(shù)(function(){})的第一對(duì)括號(hào)向腳本返回未命名的函數(shù),隨后一對(duì)空括號(hào)立即執(zhí)行返回的未命名函數(shù),括號(hào)內(nèi)為匿名函數(shù)的參數(shù)。
  • 作用:可以用它創(chuàng)建命名空間,只要把自己所有的代碼都寫在這個(gè)特殊的函數(shù)包裝內(nèi),那么外部就不能訪問(wèn),除非你允許(變量前加上window,這樣該函數(shù)或變量就成為全局)。各JavaScript庫(kù)的代碼也基本是這種組織形式。
    總結(jié)一下,執(zhí)行函數(shù)的作用主要為 匿名 和 自動(dòng)執(zhí)行,代碼在被解釋時(shí)就已經(jīng)在運(yùn)行了。
    其他寫法
(function () { /* code */ } ()); 
!function () { /* code */ } ();
~function () { /* code */ } ();
-function () { /* code */ } ();
+function () { /* code */ } ();
function前面的!想到的

最近沒(méi)事喜歡看看,一些js庫(kù)的源碼,結(jié)果發(fā)現(xiàn)庫(kù)前不是加一個(gè)!就是加+或者一個(gè)(),心中猜出個(gè)大概知道這個(gè)是讓函數(shù)自動(dòng)執(zhí)行,可是這么多符號(hào)達(dá)到同一個(gè)目的,原理是什么呢,下面做一下剖析:
先從IIFE開(kāi)始介紹
IIFE(Imdiately Invoked Function Expression 立即執(zhí)行的函數(shù)表達(dá)式)

function(){
    alert('IIFE');
}

把這個(gè)代碼放在console中執(zhí)行會(huì)報(bào)錯(cuò)


因?yàn)檫@個(gè)是一個(gè)匿名函數(shù),要想讓它正常運(yùn)行就必須給個(gè)函數(shù)名,然后通過(guò)函數(shù)名調(diào)用。
好了這下知道為啥我們看到很多類庫(kù)寫的時(shí)候也是匿名函數(shù)結(jié)果不報(bào)錯(cuò)了吧,就是因?yàn)檫@些前面加的符號(hào)的原因。
其實(shí)在匿名函數(shù)前面加上這些符號(hào)后,就把一個(gè)函數(shù)聲明語(yǔ)句變成了一個(gè)函數(shù)表達(dá)式,是表達(dá)式就會(huì)在script標(biāo)簽中自動(dòng)執(zhí)行
運(yùn)算符

  1. 為什么加上了這些運(yùn)算符后就能讓一個(gè)匿名函數(shù)變成一個(gè)不會(huì)報(bào)錯(cuò)的函數(shù)表達(dá)式呢?
    我們自然會(huì)想到j(luò)avascript的解析器到底是怎么工作識(shí)別的呢,js解析器執(zhí)行js表達(dá)式這個(gè)肯定是沒(méi)有問(wèn)題的。其實(shí)無(wú)論是括號(hào),還是感嘆號(hào),讓整個(gè)語(yǔ)句合法做的事情只有一件,就是讓一個(gè)函數(shù)聲明語(yǔ)句變成了一個(gè)表達(dá)式。所以我們讓一個(gè)函數(shù)定義變成一個(gè)函數(shù)表達(dá)式來(lái)執(zhí)行就不會(huì)報(bào)錯(cuò)。
  2. 原理
    這樣是一個(gè)函數(shù)聲明
function a(){
    alert('IIFE');
}

這樣是一個(gè)函數(shù)調(diào)用

a();

理解一下就是在一個(gè)聲明了的函數(shù)后面加上一個(gè)()就可以調(diào)用函數(shù)了

function a(){
    alert('IIFE');
}()

就這樣
但是我們按上面在console中執(zhí)行發(fā)現(xiàn)出錯(cuò)了


因?yàn)檫@樣的代碼混淆了函數(shù)聲明和函數(shù)調(diào)用,以這種方式聲明的函數(shù) a,就應(yīng)該以 a(); 的方式調(diào)用。
但是括號(hào)則不同,它將一個(gè)函數(shù)聲明轉(zhuǎn)化成了一個(gè)表達(dá)式,解析器不再以函數(shù)聲明的方式處理函數(shù)a,而是作為一個(gè)函數(shù)表達(dá)式處理,也因此只有在程序執(zhí)行到函數(shù)a時(shí)它才能被訪問(wèn)。所以,任何消除函數(shù)聲明和函數(shù)表達(dá)式間歧義的方法,都可以被解析器正確識(shí)別。所以,賦值,邏輯,甚至是逗號(hào),各種操作符都可以告訴解析器,這個(gè)不是函數(shù)聲明,它是個(gè)函數(shù)表達(dá)式。并且,對(duì)函數(shù)一元運(yùn)算可以算的上是消除歧義最快的方式,感嘆號(hào)只是其中之一,如果不在乎返回值,這些一元運(yùn)算都是有效的

!function(){alert('iifksp')}()        // true
+function(){alert('iifksp')}()        // NaN
-function(){alert('iifksp')}()        // NaN
~function(){alert('iifksp')}()        // -1

性能
針對(duì)這些一元運(yùn)算符,到底用哪個(gè)好呢,測(cè)試發(fā)現(xiàn)()的性能最優(yōu)越,但是差別都不是特明顯,所以對(duì)于一個(gè)庫(kù)來(lái)說(shuō)用幾個(gè)這樣的符號(hào)來(lái)說(shuō)看不出什么影響,所以平常用 ! + - 都可以,就看個(gè)人的代碼習(xí)慣,當(dāng)然最好還是用()。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容