JS自執(zhí)行函數(shù)又稱為IIFE,在我們開發(fā)過(guò)程中會(huì)使用到大量的自執(zhí)行函數(shù)。
IIFE寫法:
使用建議:在使用只執(zhí)行函數(shù)前面加上 “ ; ”,避免壓縮或者打包時(shí)變?yōu)楹瘮?shù)。
首先我們要了解一般情況下什么是函數(shù)聲明語(yǔ)句,什么是函數(shù)表達(dá)式語(yǔ)句,以便于接下來(lái)的實(shí)驗(yàn)。
辨別方法:以“function”開頭的有名稱的函數(shù)是函數(shù)聲明語(yǔ)句。
function a();//函數(shù)聲明語(yǔ)句
var a = function();//函數(shù)表達(dá)式語(yǔ)句
下面我們?cè)诜治鲆坏烂嬖囶}:
var a = function(){}
var b = function(){}
console.log(a()+b());//輸出結(jié)果是NaN
因?yàn)榻忉屍鲿?huì)把前面的a()認(rèn)為是一個(gè)語(yǔ)句塊的結(jié)束,后面的‘+’一元運(yùn)算符有把后面b()轉(zhuǎn)換為數(shù)字這么一個(gè)功能,所以得到的結(jié)果是NaN。
因此我們?cè)谧鲎詧?zhí)行函數(shù)的時(shí)候,要把函數(shù)的聲明變?yōu)楹瘮?shù)表達(dá)式,這樣就不會(huì)影響輸出的結(jié)果了。
方法1:
(function(){
})();
方法2:
(function(){
}() );
方法3(通過(guò)操作符):但是這種方法仍然會(huì)占用命名空間,所以不建議使用。
var a = function (){
console.log(2)
}()
方法4(通過(guò)操作符)與或操作符:
false || function (){
console.log(2)
}()
true && function (){
console.log(2)
}()
0 , function (){
console.log(2)
}()
注:通過(guò)操作符實(shí)現(xiàn)自執(zhí)行函數(shù)一般使用在打包工具里面,比如webpack打包后會(huì)經(jīng)常看到 ” 0,functtion(){}()“
方法5:(一元運(yùn)算符)一元運(yùn)算符仍然可以將函數(shù)聲明轉(zhuǎn)換為函數(shù)表達(dá)式,在bootstrap框架中常用。
! function (){
console.log(2)
}()
-function (){
console.log(2)
}()
+function (){
console.log(2)
}()
方法6:new一個(gè)匿名函數(shù),后面可省略括號(hào)。(不建議使用,會(huì)產(chǎn)生爭(zhēng)議)
new function (){
console.log(2)
};//在不傳參的情況下使用new也可以自執(zhí)行,可以去掉小括號(hào),不常用。
以上方法性能比較:
除了一元運(yùn)算符的時(shí)候性能偏低,因?yàn)橐M(jìn)行一次數(shù)字類型的轉(zhuǎn)換,其他的都可以。
應(yīng)用:
我們要了解,為什么使用自執(zhí)行函數(shù),確切的說(shuō)是自執(zhí)行函數(shù)的優(yōu)點(diǎn)以及缺點(diǎn):
1.避免作用域命名污染
2.提升性能(減少了對(duì)作用域的查找)
3.避免全局命名沖突
比如jq里面暴露給全局作用域$和query兩個(gè)變量,為了解決這個(gè)問題,我們可以將window.jq作為一個(gè)實(shí)參傳遞給一個(gè)立即執(zhí)行的匿名函數(shù)。這樣的話,我們?cè)俅蚊?或者query就不會(huì)有沖突了。
4.有利于代碼壓縮(可以用簡(jiǎn)單字符串代替)
5.保存閉包狀態(tài)
6.顛倒代碼執(zhí)行順序
(function(){})(bb())
console.log(12)
function bb(){
console.log(22)
};//打印順序是22,12
這就叫做UMD通用模塊規(guī)范,在實(shí)際開發(fā)過(guò)程中被廣泛應(yīng)用。
7.模仿塊級(jí)作用域
作用域的內(nèi)部可以訪問到外部,外部不可以訪問到內(nèi)部,js作用域的缺陷就是沒有塊級(jí)作用域。
比如:
for (var i = 0; i <6; i++) {
}
console.log(i);//打印出來(lái)的是6,在for循環(huán)外面仍然可以訪問到變量i。
三個(gè)特殊的語(yǔ)句可以欺騙語(yǔ)法分析:
evel(); //內(nèi)部存放js代碼可被執(zhí)行
with(object instance) { //代碼塊 } ; // with 語(yǔ)句可以方便地用來(lái)引用某個(gè)特定對(duì)象中已有的屬性,但是不能用來(lái)給對(duì)象添加屬性。要給對(duì)象創(chuàng)建新的屬性,必須明確地引用該對(duì)象。
try { // 此處是可能產(chǎn)生例外的語(yǔ)句 } catch(error) { // 此處是負(fù)責(zé)例外處理的語(yǔ)句 } finally { // 此處是出口語(yǔ)句 }
所以在ES3下,嚴(yán)格來(lái)說(shuō)js是沒有塊級(jí)作用域的,但是以上三種方法可以實(shí)現(xiàn)塊級(jí)作用域的效果。
常用的有try catch,把要聲明的變量放在try里面,然后當(dāng)一個(gè)錯(cuò)誤拋出,讓cath接住使用。缺點(diǎn):1.多個(gè)塊級(jí)作用域需要多個(gè)嵌套;2.性能問題。
匿名自執(zhí)行函數(shù)只能模擬塊級(jí)作用域,并非真正的作用域。
let定義的作用域是塊級(jí)作用域,但不要輕易使用,針對(duì)開發(fā)場(chǎng)景來(lái)選擇。
8.填坑(加載第三方插件時(shí)候的一些問題)
比如有個(gè)人寫了這樣一段代碼:undefined=true,再早之前undefined是可以被賦值的,所以這段代碼不會(huì)報(bào)錯(cuò),但是這樣會(huì)致使你下面的一些判斷出錯(cuò)
這時(shí)候如果使用匿名自執(zhí)行函數(shù)就可以解決:
(function(undefined){
console.log(undefined)
}())