<js> 闭包 & 立即执行函数

262 阅读3分钟

已知函数有3种形式:

  1. 函数声明(带函数名): function fn( ) {...}
  2. 匿名函数(不带函数名):function( ) {...}   (直接在.js 文件里这样写会报错,因为js 引擎不知道你要干嘛,也不存,也不执行d)
  3. 函数表达式(将匿名函数存在某变量中): const fn = function ( ) {...}


2种立即执行函数:

  1. (function (y) {...} )(x);  
  2. (function (y) {...} (x));   

// 相当于创建一个匿名函数,存在临时变量里,然后执行这个临时变量. 
// () 起的作用是告诉 js 引擎这是一个表达式  
var temp = function(y) {...};     
temp(x); 


闭包

闭包是为了让内部函数访问外部函数里定义的变量

经典问题是这样的:

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

这是怎么回事呢?

1秒后,setTimeout 内函数执行时,需要打印 i,js 层层向外找 i 的作用域,发现是在 for (var i...) 的时候声明的,i 的作用域是 window (var 是往外找 function, 一直找到window 层;let 是往外找到{ } 层),这个时候,i 的值已经自增到 3,所以输出 3,3,3。


下面改写一下:

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

发现输出还是3,3,3,怎么回事呢?是套了一层立即执行函数没错,但是到1秒后console.log打印的时候,仍然是打印变量 i,那就必须层层向外找 i 的作用域,发现还是 window,这时 i 已经变成 3了。


再改写一下:

for (var i = 0; i < 3; i++) { // 0,1,2
    (function (j) {
        setTimeout(function () {
            console.log(j);
        }, 1000)
    })(i);
}

输出 0,1,2, 怎么回事呢?1秒后console.log打印的时候,发现是要打印 j,层层往外找 j 的作用域,发现是 function (j)。参考上面谈到的立即执行函数,实际上,for 循环过程,产生了3 个临时变量(封存了函数表达式),i 的即时值传给了j 

function (j=0)(setTimeout(...)),  

function (j=1)(setTimeout(...)),  

function (j=2)(setTimeout(...)).

当然,以上代码段写成这样也行:

for (var i = 0; i < 3; i++) { // 0,1,2
    (function (j) {
        setTimeout(function () {
            console.log(j);
        }, 1000)
    }(i));
}

下面再来看es6 的let

// 把第一段代码的 var i 变成 let i
for (let i = 0; i < 3; i++) {// output 0,1,2
    setTimeout(function () {
        console.log(i);
    }, 1000);
}

输出 0,1,2, 为什么呢?let 的作用域是往外找到最近一层{ },于是console.log 打印的时候层层向外找 i,找到for(){} 花括号的地方(不同于 var 找的是function, var 的话最后找到的是window 层)。效果类似于下面这一段:

for (var i = 0; i < 3; i++) {// output 0,1,2
     const fn = function() {
        var j = i;
         setTimeout(function () {
            console.log(j);
        }, 1000);
    };
    fn(); 
}


(以上是个人理解,欢迎指正)

(时值SARI,师兄师姐去武汉上前线了,闺蜜也上前线了,广东省定点医院,网上还有人说风凉话,我心好痛,好乱,如有词不达意,见谅。

并请大家多多支持医生,他们是英雄!

如果做不到,请务必保证个人安全,出门戴口罩,回家洗手消毒,不让自己成为移动传染源,医生够忙的了。

天佑湖北,天佑中华)