image-20210406161551214

前言

在正式了解闭包之前,我们先了解一个东西——立即执行函数,那么什么是立即执行函数呢?一起看下吧!

定义

所谓的立即执行函数,顾名思义,就是一个函数可以立即执行,且执行过后将被销毁。

语法

立即执行函数有两种官方给出的写法:

// 第一种 
(function (){}())
// 第二种
(function (){})()

以上两种写法均可,但W3C官方推荐我们使用第一种方式;

作用

​ 假设我们现在有一个这样的场景,计算1 + 2 + 3 + … + 100,计算完之后拿到得到的值去进入下一步操作,那么如果我们单纯的去写一个函数的话,因为这个函数在使用一次之后,我们就不需要了,所以就会占用着内存空间,不太友好,这个时候我们的立即执行函数就派上用场了。

(function (){
    // 此处省略100000行代码
}());

看到这里我们不禁想了一下,这个函数真的会被销毁吗?是不是因为这个函数压根没有名字,这才看似被销毁了吧?按照这个疑问,我们给这个匿名函数加上一个函数名称看一下:

(function a(){
    // 此处省略100000行代码
}());
console.log(a) // Uncaught ReferenceError: a is not defined

那么立即执行函数如何传参呢?

let a = (function a(a, b, c){ // 形参
   let d = a + b + c * 2;
   return d
}(1, 2, 3)); // 实参在这里
console.log(a); // 9

深度挖掘

接下来,请记住这样一句话:

只有表达式才能被执行符号( 就是传说中的花括号 )执行

首先,我们先来一串代码看看效果

// 正常函数
function test() {
    console.log(1);
};
test(); // 1
// 反人类函数
function test() {
    console.log(1);
}() // 报语法错误

由上面的代码我们可以知道,正常函数我们直接去执行是没有问题的。但是反人类函数就执行不出来了,这就要联系到刚才我们所说的那句话,只有表达式才能被执行符号( 就是传说中的花括号 )执行。

再来看看下面这串代码:

var test = function() {
    console.log('test');
}
test() // 'test'

上述代码大家理解肯定没有问题,声明一个匿名函数赋给test变量,然后test执行,打印结果。注意:此时这个test = function(){} 就是一个表达式,所以可以执行。

+ function test() {
    console.log('我竟然能执行!!!')
}()

​ 上面的代码竟然能执行!!!这是为什么呢?还是那句话,因为当我们前面添加了“+”后,这串代码就变成了一个表达式了,表达式才能被执行符号执行,所以这个是可以执行的,类似于“-”等等只要是能把函数声明转换成表达式的操作都可以被执行。于是,形成了我们现在推荐的那种立即执行函数的写法。

(function test(){
    // 此处省略100000行代码
}());

原理:因为括号也是一种运算符,所以添加它之后,我们的函数就变成了表达式,所以能被执行,后来又因为执行过后函数被销毁,导致了我们给函数添加名称和不添加的效果是一样的,所以索性直接去掉函数名称,于是最后发展成了现在我们看到的W3C推荐的格式!

阿里面试题

function test(a, b, c, d) {
    console.log(a + b + c + d);
}(1, 2, 3, 4)
// 能否执行?

在正常看来,如果不带参数,这串代码绝对不能执行,但是,这道题恶心就恶心在你对执行符号的理解程度,“()”这样就是单纯的去执行某个函数,但是()内部一旦传入参数,它就不是执行符号了,这串代码就变成了这样:

function test(a, b, c, d) {
    console.log(a + b + c + d);
}

(1, 2, 3, 4)
// 变成了分开执行

所以这里既不会报错,有没有相应的输出,总结了这么多,有没有感觉小小的立即执行函数也能挖出来那么多有意思的事。

链接:你不知道的JS——闭包篇(下篇)