摘要:函數名可以省略省略函數名的話該函數就成為了匿名函數被傳入函數的參數的名稱一個函數最多可以有個參數這些語句組成了函數的函數體。使用那我們通常為什么使用函數立即表達式呢,以及我如何使用呢通常情況下,只對匿名函數使用這種立即執行的函數表達式。
注:此文只在理解立即執行函數,不在所謂原創,文中大量引用阮一峰的JavaScript標準參考教程、MDN的JavaScript 參考文檔和深入理解JavaScript系列(4):立即調用的函數表達式的內容。
描述立即執行函數通常有下面兩種寫法:
(function(){ ... })();
(function(){ ... }());
在Javascript中,一對圓括號“()”是一種運算符,跟在函數名之后,表示調用該函數。比如,print()就表示調用print函數。
這個寫法和我們想象的寫法不一樣(知道的人當然已經習以為常)
很多人剛開始理解立即執行函數的時候,覺得應該是這樣的:
function (){ ... }(); //或者 function fName(){ ... }();
然而事實卻是這樣:SyntaxError: Unexpected token (。這是為什么呢?
解釋要理解立即執行函數,需要先理解一些函數的基本概念:函數聲明、函數表達式,因為我們定義一個函數通常都是通過這兩種方式
函數聲明 (function 語句)
function name([param[, param[, ... param]]]) { statements }
name:函數名;
param:被傳入函數的參數的名稱,一個函數最多可以有255個參數;
statements:這些語句組成了函數的函數體。
函數表達式 (function expression)
函數表達式和函數聲明非常類似,它們甚至有相同的語法。
function [name]([param] [, param] [..., param]) { statements }
name:函數名,可以省略,省略函數名的話,該函數就成為了匿名函數;
param:被傳入函數的參數的名稱,一個函數最多可以有255個參數;
statements:這些語句組成了函數的函數體。
下面我們給出一些栗子說明:
// 聲明函數f1 function f1() { console.log("f1"); } // 通過()來調用此函數 f1(); //一個匿名函數的函數表達式,被賦值給變量f2: var f2 = function() { console.log("f2"); } //通過()來調用此函數 f2(); //一個命名為f3的函數的函數表達式(這里的函數名可以隨意命名,可以不必和變量f3重名),被賦值給變量f3: var f3 = function f3() { console.log("f2"); } //通過()來調用此函數 f3();
上面所起的作用都差不多,但還是有一些差別
1、函數名和函數的變量存在著差別。函數名不能被改變,但函數的變量卻能夠被再分配。函數名只能在函數體內使用。倘若在函數體外使用函數名將會導致錯誤:
var y = function x() {}; alert(x); // throws an erro
2、函數聲明定義的函數可以在它被聲明之前使用
foo(); // alerts FOO! function foo() { alert("FOO!"); }
但函數聲明非常容易(經常是意外地)轉換為函數表達式。當它不再是一個函數聲明:
成為表達式的一部分
不再是函數或者script自身的“源元素” (source element)。在script或者函數體內“源元素”并非是內嵌的語句(statement)
var x = 0; // source element if (x == 0) { // source element x = 10; // 非source element function boo() {} // 非 source element } function foo() { // source element var y = 20; // source element function bar() {} // source element while (y == 10) { // source element function blah() {} // 非 source element y++; //非source element } }
Examples:
// 函數聲明 function foo() {} // 函數表達式 (function bar() {}) // 函數表達式 x = function hello() {} if (x) { // 函數表達式 function world() {} } // 函數聲明 function a() { // 函數聲明 function b() {} if (0) { //函數表達式 function c() {} } }
現在我們來解釋上面的SyntaxError: Unexpected token (:
產生這個錯誤的原因是,Javascript引擎看到function關鍵字之后,認為后面跟的是函數定義語句,不應該以圓括號結尾。
解決方法就是讓引擎知道,圓括號前面的部分不是函數定義語句,而是一個表達式,可以對此進行運算。所以應該這樣寫:
(function(){ /* code */ }()); // 或者 (function(){ /* code */ })();
這兩種寫法都是以圓括號開頭,引擎就會認為后面跟的是一個表示式,而不是函數定義,所以就避免了錯誤。這就叫做“立即調用的函數表達式”(Immediately-Invoked Function Expression),簡稱IIFE。
注意,上面的兩種寫法的結尾,都必須加上分號。
推而廣之,任何讓解釋器以表達式來處理函數定義的方法,都能產生同樣的效果,比如下面三種寫法。
var i = function(){ return 10; }(); true && function(){ /* code */ }(); 0, function(){ /* code */ }();
甚至像這樣寫:
!function(){ /* code */ }(); ~function(){ /* code */ }(); -function(){ /* code */ }(); +function(){ /* code */ }();
new關鍵字也能達到這個效果:
new function(){ /* code */ } new function(){ /* code */ }() // 只有傳遞參數時,才需要最后那個圓括號。使用
那我們通常為什么使用函數立即表達式呢,以及我如何使用呢?
通常情況下,只對匿名函數使用這種“立即執行的函數表達式”。
它的目的有兩個:
一是不必為函數命名,避免了污染全局變量;
二是IIFE內部形成了一個多帶帶的作用域,可以封裝一些外部無法讀取的私有變量。
// 寫法一 var tmp = newData; processData(tmp); storeData(tmp); // 寫法二 (function (){ var tmp = newData; processData(tmp); storeData(tmp); }());
上面代碼中,寫法二比寫法一更好,因為完全避免了污染全局變量。
最后在舉一個真實的栗子:在JavaScript的OOP中,我們可以通過IIFE來實現一個單例(關于單例的優化不再此處討論)
// 創建一個立即調用的匿名函數表達式 // 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只存在于閉包)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/91548.html
摘要:要理解立即執行函數,需要先理解一些函數的基本概念。函數表達式使用關鍵字聲明一個函數,但未給函數命名,最后將匿名函數賦予一個變量,叫函數表達式,這是最常見的函數表達式語法形式。 javascript和其他編程語言相比比較隨意,所以javascript代碼中充滿各種奇葩的寫法,有時霧里看花,當然,能理解各型各色的寫法也是對javascript語言特性更進一步的深入理解。 ( functio...
摘要:將匿名函數賦予一個變量,叫函數表達式,這是最常見的函數表達式語法形式。組成這是一個被稱為自執行匿名函數的設計模式,主要包含兩部分。 一、函數聲明&函數表達式 1.1 函數聲明 (函數語句) showImg(https://segmentfault.com/img/bVbbqvT?w=278&h=166); (1)使用 function 關鍵字聲明一個函數,再指定一個函數名,叫函數聲明。...
摘要:模仿塊級作用域立即執行函數前言最近在細讀高級程序設計,對于我而言,中文版,書中很多地方一筆帶過,所以用自己所理解的,嘗試細致解讀下。語法如下這里是塊級作用域以上代碼定義并立即調用了一個匿名函數。 模仿塊級作用域-立即執行函數 前言:最近在細讀Javascript高級程序設計,對于我而言,中文版,書中很多地方一筆帶過,所以用自己所理解的,嘗試細致解讀下。如有紕漏或錯誤,會非常感謝您的指...
摘要:匿名函數是不能單獨寫的,所以就提不上立即執行了。六立即執行函數在閉包中的應用立即執行函數能配合閉包保存狀態。來看下上節內容中閉包的例子現在,我們來利用立即執行函數來簡化它第一個匿名函數執行完畢后,返回了第二個匿名函數。 前面的閉包中,提到與閉包相似的立即執行函數,感覺兩者還是比較容易弄混吧,嚴格來說(因為犀牛書和高程對閉包的定義不同),立即執行函數并不屬于閉包,它不滿足閉包的三個條件。...
摘要:前言大家學的時候,經常遇到自執行匿名函數的代碼,今天我們主要就來想想說一下自執行。其實,前面兩個例子里的變量,也可以換成,因為和外面的不在一個作用于,所以不會出現問題,這也是匿名函數閉包的威力。 前言 大家學JavaScript的時候,經常遇到自執行匿名函數的代碼,今天我們主要就來想想說一下自執行。 在詳細了解這個之前,我們來談了解一下自執行這個叫法,本文對這個功能的叫法也不一定完全對...
閱讀 2813·2023-04-25 15:01
閱讀 3012·2021-11-23 10:07
閱讀 3358·2021-10-12 10:12
閱讀 3444·2021-08-30 09:45
閱讀 2184·2021-08-20 09:36
閱讀 3566·2019-08-30 12:59
閱讀 2424·2019-08-26 13:52
閱讀 927·2019-08-26 13:24