JS之立即執行函數講解
本文主要和大家分享JS之立即執行函數講解,所謂立即執行函數(Immediately-Invoked Function Expression),即在函數表達式之後加()就可以使函數自執行。注意:不能再函數宣告後加括號()。
(function foo(){/* code */}()); //正确,推荐这样使用 (function foo(){/* code */})(); //正确,这样可以 var foo = function foo(){/* code */}(); //正确 function foo(){/* code */}(); //SyntaxError: Unexpected token ( // 但是如果你在括弧()里传入一个表达式,将不会有异常抛出 // 但是foo函数依然不会执行 function foo(){ /* code */ }( 1 ); // 因为它完全等价于下面这个代码,一个function声明后面,又声明了一个毫无关系的表达式: function foo(){ /* code */ } ( 1 ); // 由于括弧()和JS的&&,异或,逗号等操作符是在函数表达式和函数声明上消除歧义的 // 所以一旦解析器知道其中一个已经是表达式了,其它的也都默认为表达式了 // 不过,请注意下一章节的内容解释 var i = function () { return 10; } (); true && function () { /* code */ } (); 0, function () { /* code */ } (); // 如果你不在意返回值,或者不怕难以阅读 // 你甚至可以在function前面加一元操作符号 !function () { /* code */ } (); ~function () { /* code */ } (); -function () { /* code */ } (); +function () { /* code */ } (); // 还有一个情况,使用new关键字,也可以用,但我不确定它的效率 // http://twitter.com/kuvos/status/18209252090847232 new function () { /* code */ } new function () { /* code */ } () // 如果需要传递参数,只需要加上括弧()
作用域
IIFE可為局部變數提供一個封裝的作用域,並在IIFE回傳的函數中能夠存取此變數。這種方式即允許函數存取這個本地變數,即使這個函數在IIFE的詞法範圍之外執行時。
const uniqueId1 = function() { let count1 = 0; return function() { ++count1; return count1; }; }; uniqueId1(); //ƒ () {++count1;return count1;}
const uniqueId2 = function() { let count = 0; return count; }; uniqueId2(); //0 uniqueId2(); //0 uniqueId2(); //0
const uniqueId3 = (function() { let count3 = 0; return function() { ++count3; return count3; }; })(); uniqueId3(); //1 uniqueId3(); //2 uniqueId3(); //3 uniqueId3(); //4
const uniqueId4 = function() { let count4 = 0; return (function() { ++count4; return count4; })(); }; uniqueId4(); //1 uniqueId4(); //1 uniqueId4(); //1 uniqueId4(); //1
##注意:在IIFE之外無法存取這個計數變數count。除了從IIEF傳回的函數,別人無法讀寫該變數。這樣就能創造出真正的私有狀態,它只能以受控的方式進行修改。
當使用IIFE來傳回一個」封閉」一些本地變數來管理私有資料的函數時,let和##const
都不能取代它。
// 创建一个立即调用的匿名函数表达式 // return一个变量,其中这个变量里包含你要暴露的东西 // 返回的这个变量将赋值给counter,而不是外面声明的function自身 var counter = (function () { var i = 0; return { get: function () { return i; }, set: function (val) { i = val; }, increment: function () { return ++i; } }; } ()); // counter是一个带有多个属性的对象,上面的代码对于属性的体现其实是方法 counter.get(); // 0 counter.set(3); counter.increment(); // 4 counter.increment(); // 5 counter.i; // undefined 因为i不是返回对象的属性 i; // 引用错误: i 没有定义(因为i只存在于闭包)
閉包與IIFE
# 和一般function執行的時候傳參數一樣,自執行的函數表達式也可以這麼傳參,因為閉包直接可以引用傳入的這些參數,利用這些被lock住的傳入參數,自執行函數表達式可以有效地保存狀態。
// 这个代码是错误的,因为变量i从来就没背locked住 // 相反,当循环执行以后,我们在点击的时候i才获得数值 // 因为这个时候i操真正获得值 // 所以说无论点击那个连接,最终显示的都是I am link #10(如果有10个a元素的话) var elems = document.getElementsByTagName('a'); for (var i = 0; i < elems.length; i++) { elems[i].addEventListener('click', function (e) { e.preventDefault(); alert('I am link #' + i); }, 'false'); } // 这个是可以用的,因为他在自执行函数表达式闭包内部 // i的值作为locked的索引存在,在循环执行结束以后,尽管最后i的值变成了a元素总数(例如10) // 但闭包内部的lockedInIndex值是没有改变,因为他已经执行完毕了 // 所以当点击连接的时候,结果是正确的 var elems = document.getElementsByTagName('a'); for (var i = 0; i < elems.length; i++) { (function (lockedInIndex) { elems[i].addEventListener('click', function (e) { e.preventDefault(); alert('I am link #' + lockedInIndex); }, 'false'); })(i); } // 你也可以像下面这样应用,在处理函数那里使用自执行函数表达式 // 而不是在addEventListener外部 // 但是相对来说,上面的代码更具可读性 var elems = document.getElementsByTagName('a'); for (var i = 0; i < elems.length; i++) { elems[i].addEventListener('click', (function (lockedInIndex) { return function (e) { e.preventDefault(); alert('I am link #' + lockedInIndex); }; })(i), 'false'); }
問題1、
for (var i = 0; i < 5; i++) { setTimeout(function(i) { console.log(i); }, i * 1000); }
問題2、
for (var i = 0; i < 5; i++) { setTimeout((function(i) { console.log(i); })(i), i * 1000); }
問題3、
for (var i = 0; i < 5; i++) { setTimeout((function(i) { return function() { console.log(i); } })(i), i * 1000); }
問題4、
for (var i = 0; i < 5; i++) { setTimeout((function(i) { console.log(i); }).bind(this,i), i * 1000); }
建議寫法:
var j = 0; for (i = 0; i < 5; i++) { setTimeout(function() { console.log(j); j++; }, i * 1000); }
之所以用到立即呼叫/執行的函數表達式,主要作用是 模仿區塊級作用域(也叫私有作用域):避免在全域作用域中加入過多的變數和函數。這樣每個開發人員既可以使用自己的變量,又不必擔心會影響或搞亂全域作用域。
IIFE打包壓縮
1、參數變短
IIFE可以進行參數變數名稱混疊,如下:
window.$ = function somethingElse() { // ... }; (function($) { // ... })(jQuery);
为了解决命名冲突问题,可以将一段代码封装在一个IIEF中,将一个全局变量(比如,jQuery)作为参数传入IIFE。在函数内部,就可以以一个任意的参数名(比如,$)来访问该参数值。
IIFE的这种特性可以用来优化代码,这种方式使代码能够被更有效的压缩。例如:
(function(window, document, undefined) { // ... })(window, document);
可以缩短函数的参数名为单个字母的标识符,更短标识符名会使文件的体积变得更小。
(function(w, d, u) { // ... })(window, document);
2、括号和分号的使用
我们知道,以下两种方法都是立即执行函数的写法:
// 下面2个括弧()都会立即执行 (function () { /* code */ } ()); (function () { /* code */ })();
注意这两种写法:匿名函数上面的写法都存在前后文;问题,所以需要注意的是匿名函数在压缩工具打包压缩后会出现上下文错误合并()的问题,例如第二种写法。如果下面的代码,未压缩之前是正常的,压缩后就不正常了,所以要严格上下文的;问题,而第一种就不会出现类似问题。
var a="bbb" (function(){ alert(1); })();
//var a = function(){} var a="bbb" (function(){ alert(1); }());
上述代码会报""bbb" is not a function"错误,若变量a是一函数,则会报"undefined is not a function",这是因为a变量或a函数会把他后面的匿名函数作为参数传入a中,这就很好的解释了为什么报前面提到的错误,这也很好地解释了为什么有人习惯在匿名函数之前添加;了,就是为了防止上文没有严格遵循javascript语法,漏掉;的问题。
相关推荐:
以上是JS之立即執行函數講解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Go語言提供了兩種動態函數創建技術:closures和反射。 closures允許存取閉包作用域內的變量,而反射可使用FuncOf函數建立新函數。這些技術在自訂HTTP路由器、實現高度可自訂的系統和建置可插拔的元件方面非常有用。

在C++函數命名中,考慮參數順序至關重要,可提高可讀性、減少錯誤並促進重構。常見的參數順序約定包括:動作-物件、物件-動作、語意意義和遵循標準函式庫。最佳順序取決於函數目的、參數類型、潛在混淆和語言慣例。

1. SUM函數,用於對一列或一組單元格中的數字進行求和,例如:=SUM(A1:J10)。 2、AVERAGE函數,用於計算一列或一組儲存格中的數字的平均值,例如:=AVERAGE(A1:A10)。 3.COUNT函數,用於計算一列或一組單元格中的數字或文字的數量,例如:=COUNT(A1:A10)4、IF函數,用於根據指定的條件進行邏輯判斷,並返回相應的結果。

C++函數中預設參數的優點包括簡化呼叫、增強可讀性、避免錯誤。缺點是限制靈活性、命名限制。可變參數的優點包括無限彈性、動態綁定。缺點包括複雜性更高、隱式型別轉換、除錯困難。

C++中的函數傳回參考類型的好處包括:效能提升:引用傳遞避免了物件複製,從而節省了記憶體和時間。直接修改:呼叫方可以直接修改傳回的參考對象,而無需重新賦值。程式碼簡潔:引用傳遞簡化了程式碼,無需額外的賦值操作。

自訂PHP函數與預定義函數的差異在於:作用域:自訂函數僅限於其定義範圍,而預定義函數可在整個腳本中存取。定義方式:自訂函數使用function關鍵字定義,而預先定義函數則由PHP核心定義。參數傳遞:自訂函數接收參數,而預先定義函數可能不需要參數。擴充性:自訂函數可以根據需要創建,而預定義函數是內建的且無法修改。

C++中的異常處理可透過自訂異常類別增強,提供特定錯誤訊息、上下文資訊以及根據錯誤類型執行自訂操作。定義繼承自std::exception的異常類,提供特定的錯誤訊息。使用throw關鍵字拋出自訂異常。在try-catch區塊中使用dynamic_cast將捕獲到的異常轉換為自訂異常類型。在實戰案例中,open_file函數會拋出FileNotFoundException異常,捕捉並處理該異常可提供更具體的錯誤訊息。
