函数的IIFE

200 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

前言

函数,无人不知,无人不晓得。 有一些基础的概念是必须知道的,今天一起学习IIFE吧。

定义

IIFE( 立即调用函数表达式)是一个在定义时就会立即执行的  JavaScript 函数

最常见的形式:

;(function () {
    statements
})();

当然,在文件的开头加一个;绝对不是坏事,现代化的代码合并工具是很智能的,ES6也是允许一行有效的代码之后不添加;, 但是也是与例外的。

下面的代码是执行会报错

var arr = []
[1,2,3].map(v=>v*v)

// Uncaught TypeError: Cannot read properties of undefined (reading 'map')

添加一个;号,就没问题了

var arr = [];
[1,2,3].map(v=>v*v)
// (3) [1, 4, 9]

至于是为什么,嘻嘻,先保密!

IIFE的作用

避免污染全局作用域

避免了外界访问此 IIFE 中的变量,而且又不会污染全局作用域。

比如浏览器环境下:

var a = 10;

// 如果是浏览器
console.log(window.a); // 10
console.log(a); // 10

a挂到了window下面,成为了全局的变量,这就是全局污染。

如果这样到处乱申明,很可能覆盖其他的变量,是很危险的, 大名鼎鼎的jQuery因此还搞了专门处理名称冲突的方法。

var jq=$.noConflict();

减少内存占用

IIFE执行完毕后,不被引用的变量是会被回收的,可以减少不必要的内存占用。

比如我们就打印一下浏览器的userAgen信息


// 非IIFE
function printUserAgent(){
    console.log(navigator.userAgent);
}
printUserAgent();
console.log(typeof printUserAgent); // function


// IIFE
;(function (){
    function printUserAgent(){
        console.log(navigator.userAgent);
    }
    printUserAgent();
})()
console.log(typeof printUserAgent); // undefined

非IIFE部分的代码,执行完毕后,printUserAgent依旧存在,而IIFE的执行了回收。

形成块作用域

经典的题目, 就是用IIFE + 闭包形成私有作用域。

// 非IIFE
for (var i = 0; i < 5; i++) {
  setTimeout(function () {
    console.log(i);
  }, 1000);
}

// 5 5 5 5 5 


for (var i = 0; i < 5; i++) {
  (function (v) {
    setTimeout(function () { console.log(v) }, 1000)
  })(i);
}

// 0 1 2 3 4

带返回值

最后直接return 就行

var utils = (function (){
    return {
        get now(){
            return Date.now();
        }
    }
})();

console.log(utils.now);  // 1633438917346

初始化参数

var utils = (function (global){
    return {
        get now(){
            return Date.now();
        },
        getGlobal(){
            return global
        }
    }
})(window);

console.log("global:", utils.getGlobal());
// Window {window: Window, self: Window, document: document, name: '', location: Location, …}

其他IIFE形式

其实有很多形式的IIFE, 基本任何求值得均可以达到目标,当然,可能有些不能有效的传递参数。

+ function log (){
   console.log("1111");
}() // 1111

是不是很有意思,其他的-~, !void等等符号均可形成IIFE。

有趣的求值符号

看点有意思的:

function log (){
    console.log("1111");
}() 
// Uncaught SyntaxError: Unexpected token ')'

function log (){
    console.log("1111");
}(
// Uncaught SyntaxError: Unexpected end of input

function log (){
    console.log("1111");
}(1)
// 1

;() 
// Uncaught SyntaxError: Unexpected token ')'

可以看出来,前面没有求值符号的时候, 引擎的解析,对函数解析完毕后,以(开始,作为新解析的开始。

但是有求值符号后,等同于如下:

+ (function log (){
   console.log("1111");
}())

是不是有些意思。

更进一步的解读,用得用AST看看了。

小结

今天你收获了吗?