使用立即调用的函数表达式创建局部作用域

256 阅读2分钟

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

先来看一段代码输出的是什么?

 function test(arr) {
   var result = [];
   for (var i = 0; i < arr.length; i++) {
     result[i] = function() {
       return arr[i];
     };
   }
   return result;
 }
 var result = test([10, 20, 30]);
 var a = result[0];
 console.log(a());  // undefined

我们希望输出的是 10 但是程序给我们的结果却是 undefined

for循环中头部使用 var 定义变量 i 时,它和循环体中的 i 的作用域不是同级作用域,因此当 for 循环执行完毕后,并不会给每个循环都储存相对应的 i 的值。

我们似乎期望该函数存储的是嵌套函数创建时变量 i 的值。但事实上,它存储的是变量 i 的引用。由于每次函数创建后变量i的值都发生了变化,因此内部函数最终看到的是变量 i 最后的值。需要注意的是,闭包存储的是其外部变量的引用而不是值。

解决方案一 : 立即调用函数

 function test2(arr) {
   var result = [];
   for (var i = 0; i < arr.length; i++) {
     // 强制创建作用域
     (function(i) {
       result[i] = function() {
         return arr[i];
       };
     })(i);
   }
   return result;
 }
 var result = test2([10, 20, 30]);
 var a = result[0];
 console.log(a()); // 10

使用立即调用函数虽然可以解决问题但是要注意,代码块不能包含任何跳出块的 break语句和 continue语句。

解决方案二 : 使用 ES6 的 let 声明变量

 function test(arr) {
   var result = [];
   for (let i = 0; i < arr.length; i++) {
     result[i] = function() {
       return arr[i];
     };
   }
   return result;
 }
 var result = test([10, 20, 30]);
 var a = result[0];
 console.log(a());  // undefined

总结

  • 理解绑定与赋值的区别。
  • 闭包存储的是外部变量的引用,而不是它们的值的副本
  • 使用立即调用的函数表达式(IIFE)来创建局部作用域。
  • 当心在立即调用的函数表达式中包裹代码块可能改变其行为的情形。
  • 使用 let 吧,求求了