JavaScript之深入理解立即調用函數表達式(IIFE),你對它的理解,決定了它的出鏡率(系列六)

立即調用函數

本篇文章,主要講解的立即執行函數或自執行匿名函數的含義、用法、以及使用它的主要場景。系列的前面幾篇文章主要講解了作用域、原型、執行上下文,本篇文章一樣起到了承上啓下的作用,如果您感興趣,不妨去看看哦~ 傳送門

目錄

一、瞭解立即調用函數表達式

1.1 思維導圖

在這裏插入圖片描述

1.2 什麼是立即調用?

在詳細瞭解這個之前,我們來談了解一下“自執行”這個叫法,本文對這個功能的叫法也不一定完全對,每個人對他的理解都不一樣,我們在這裏用立即調用

立即調用:

  • 顧名思義,該表達式一被創建就立即執行
  • 是一個在定義時就會立即執行的 JavaScript 函數
(function (x) {
    console.log('x + x = ', x + x);
})(5) // x + x = 10

這是一個被稱爲 自執行匿名函數 的設計模式,主要包含兩部分:

  1. 第一部分是包圍在 圓括號運算符 () 裏的一個匿名函數,這個匿名函數擁有獨立的詞法作用域。這不僅避免了外界訪問此 IIFE 中的變量,而且又不會污染全局作用域。
  2. 第二部分再一次使用 () 創建了一個立即執行函數表達式,JavaScript 引擎到此將直接執行函數。
1.3 核心問題

當你聲明一個函數的時候,通過在後面加括號就可以實現立即執行嗎?

var foo = function(){ 
	console.log('餘光');
}(); // 餘光 成功了!
 
// ...是不是意味着後面加個括弧都可以自動執行?
function(){
	console.log(''餘光);
}(); // Uncaught SyntaxError: Function statements require a function name
// 什麼?還需要一個函數名?不是叫 自執行匿名函數嗎?

// 我加上了函數名
function foo(){
	console.log('餘光');
}(); // Uncaught SyntaxError: Unexpected token ')'

很顯然,例子中的第二條和第三條確實報錯了,而且報錯內容不一樣,那麼問題出現在哪呢?

在這裏插入圖片描述

二、立即調用函數表達式報錯了?

有時,我們定義函數之後,立即調用該函數,這時不能在函數的定義後面直接加圓括號,這會產生語法錯誤。產生語法錯誤的原因是,function 這個關鍵字,既可以當做語句,也可以當做表達式,比如下邊:

//語句
function fn() {};

//表達式
var fn = function (){};

爲了避免解析上的歧義,JS引擎規定,如果function出現在行首,一律解析成語句。因此JS引擎看到行首是function關鍵字以後,認爲這一段都是函數定義,不應該以括號結尾,在它看來括號只是分組操作符。

// 下面這個function在語法上是沒問題的,但是依然只是一個語句
// 加上括號()以後依然會報錯,因爲分組操作符需要包含表達式
 
function foo(){ /* code */ }(); // SyntaxError: Unexpected token )
 
// 但是如果你在括弧()裏傳入一個表達式,將不會有異常拋出
// 但是foo函數依然不會執行
function foo(){ /* code */ }( 1 );
 
// 因爲它完全等價於下面這個代碼,一個function聲明後面,又聲明瞭一個毫無關係的表達式: 
function foo(){ /* code */ }
 
( 1 );

在這裏插入圖片描述

三、使用立即調用函數的正確姿勢

要解決上述問題,非常簡單。

我們只需要用大括弧將代碼的代碼全部括住就行了,因爲JavaScript裏括弧()裏面不能包含語句,所以在這一點上,解析器在解析function關鍵字的時候,會將相應的代碼解析成function表達式,而不是function聲明。

3.1 常見使用姿勢
// 下面2個括弧()都會立即執行

(function () { /* code */ } ()); // 推薦使用這個
(function () { /* code */ })(); // 但是這個也是可以用的
3.2 不常見的使用姿勢(一)
// 由於括弧()和JS的&&,異或,逗號等操作符是在函數表達式和函數聲明上消除歧義的
// 所以一旦解析器知道其中一個已經是表達式了,其它的也都默認爲表達式了

var i = function() {
    console.log('餘光')
}(); // 餘光

true && function() {
    console.log('餘光')
}(); // 餘光

0, function() { console.log('餘光') }(); // 餘光
3.3 不常見的使用姿勢(二)
// 如果你不在意返回值,或者不怕難以閱讀
// 你甚至可以在function前面加一元操作符號

//轉bool
var res1 = !function () {
    console.log('餘光');
}()
console.log('res1:', res1); // 餘光 true

// 轉數字
var res2 = +function () {
    console.log('餘光');
}()
console.log('res2:', res2); // 餘光 NaN

// 按位非
var res3 =function () {
    console.log('餘光');
}()
console.log('res3:', res3); // 餘光 NaN
3.4 不常見的使用姿勢(三)

還有一個情況,使用new和void關鍵字,不過不太常見罷了。

void function() {
    console.log('餘光');
}();

new function() {
    console.log('餘光');
}();

四、常見使用場景

4.1 隔離作用域

IIFE最常見的功能,就是隔離作用域,在ES6之前JS原生也沒有塊級作用域的概念,所以需要函數作用域來模擬。

舉例:

var currentTime = (function () {
    var time = new Date();
    var year  = time.getFullYear()
    var month = time.getMonth()+1;
    var date  = time.getDate();
    var hour  = time.getHours();
    var min   = time.getMinutes();
    return year + '-' + month + '-' + date + ' ' + hour + ':' + min;
})()

你仍然可以在其他地方聲明同名變量~

4.2 惰性函數

DOM事件添加中,爲了兼容現代瀏覽器和IE瀏覽器,我們需要對瀏覽器環境進行一次判斷:

var addEvent = (function(){
    if(window.addEventListener) {
        return function(type, el, fn) {
            el.addEventListener(type, fn, false);
        }
    }
    else if(window.attachEvent) {
        return function(type, el, fn) {
            el.attachEvent('on' + type, fn);
        }
    }
})();
4.3 用閉包保存狀態

這裏我僅舉個例子,爲我的下一篇文章——《JavaScript中的閉包》賣個關子

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);
}
注意

當函數變成立即執行的函數表達式時,表達式中的變量不能從外部訪問。

(function () { 
    var name = "Barry";
})();
// 無法從外部訪問變量 name
name // 拋出錯誤:"Uncaught ReferenceError: name is not defined"

將 IIFE 分配給一個變量,不是存儲 IIFE 本身,而是存儲 IIFE 執行後返回的結果。

var result = (function () { 
    var name = "Barry"; 
    return name; 
})(); 
// IIFE 執行後返回的結果:
result; // "Barry"

參考

在這裏插入圖片描述

寫在最後

JavaScript內功系列:

  1. this、call、apply詳解,系列(一)
  2. 從原型到原型鏈,系列(二)
  3. 從作用域到作用域鏈,系列(三)
  4. JavaScript中的執行上下文(四)
  5. JavaScript中的變量對象(五)
  6. 本文
  7. JavaScript中的閉包

關於我

  • 花名:餘光
  • WX:j565017805
  • 沉迷JS,水平有限,虛心學習中

其他沉澱

如果您看到了最後,不妨收藏、點贊、評論一下吧!!!
持續更新,您的三連就是我最大的動力,虛心接受大佬們的批評和指點,共勉!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章