经典面试题之for 循环和闭包

1,748 阅读2分钟

前言

最近又温故而知新了一遍JavaScript的闭包,前面几年懵懵懂懂看了很多篇文章,今天再看还是受益匪浅,从而也搞懂了一道经典面试题,这个面试题之前也迟迟不理解,直到今天才豁然开朗。接下来就来逐步分析分析这道经典面试题。

题目

1: var data = []; 
2: for (var i = 0; i < 3; i++) { 
3:     data[i] = function () { 
4:         console.log(i); 
5:     }; 
6: }
7:
8: data[0](); // 3 
9: data[1](); // 3
10:data[2](); // 3

分析:

  1. 开始 JavaScript执行的是全局上下文作用域。

  2. 第一行声明的全局变量 data,并赋值为一个空数组

  3. 2-6行 定义了一个for 循环体,此时重点就在这个循环体里面了,再细细拆分来看。

  4. 循环体中 var i = 0; 声明的也是全局变量i < 3; i++ 是循环条件。

  5. 循环体里面 变量data 开始进行被赋值,而且是经过了三次赋值,其结果拆开来写就是:

    data[0] = function(){
        console.log(i)
    }
    data[1] = function(){
        console.log(i)
    }
    data[2] = function(){
        console.log(i)
    }
    

    此时注意 三次循环赋值 的结果的函数体内都是 console.log(i) ,而并不是console.log(0)console.log(1)console.log(2),此时的三个函数都是声明中,并未开始调用执行;

  6. 真正执行函数是 8 - 9 行,单独拿出每个数组中的函数执行。

  7. 再结合 全局声明的var i = 0; 经过三次循环,全局的 i 等于 3 了。

  8. 最后 调用函数体内的 console.log(i),此时的 i 找到的就是 全局变量 i = 3,最后输出执行的都是 console.log(3) 这个结果。

分析原因

为什么会造成这样的结果。在没了解到闭包之前,还傻傻的以为输出的是 0,1,2,但因为词法作用域的原因,导致了这样的结果,因为污染了全局变量,导致函数作用域一层一层往上找的过程,没有找到对应点变量,就找了全局的变量。

解决方案

相信网上也有很多这类面试题的解法,我也不过多描述,主要氛围异步setTimeOut处理,和ES6 let声明处理。

setTimeOut 自执行函数和闭包

var data = []; 
for (var i = 0; i < 3; i++) { 
    (function(i){ 
        setTimeout(data[i] = function () { 
            console.log(i); 
        }, 0) 
    })(i) 
} 

data[0](); 
data[1]();
data[2]();

用 let

 var data = []; 
 for (let i = 0; i < 3; i++) { 
    data[i] = function () { 
         console.log(i); 
    }
} 

data[0](); 
data[1]();
data[2]();

最后

经过这次理解函数的闭包,再结合这道经典面试题,让我更深刻的理解了一遍闭包中的全局作用域词法作用域。突然发现好多原理性的东西都是需要多看几遍才能深刻理解,每次看都有或多或少的收货。