在for循环体内进行条件改变的一个坑

先说一个众所周知的作用域问题,看一下代码:

var a = [];
for (var i = 0; i < 5; i++) {
  a.push(function () {
    console.log(i)
  });
}
a[0]();
a[1]();

以上期望输出0, 1,实际输出则是 5,5。因为i的作用域是window(global)全局。

ES6有个解决办法,把var关键字改为let关键字,即可解决作用域问题。原理可以通过babel转换一下源码探知:

var a = [];
var _loop = function _loop(i) {
  a.push(function () {
    console.log(i);
  });
};
// 源码是let声明,此处经过了babel转换
for (var i = 0; i < 5; i++) {
  _loop(i);
}

通过循环体封装为_loop函数,把i转为一个函数内部变量,不再受循环外变量影响。

而此时也该点题了,本文题目说的是另外一个隐藏的坑。见以下代码:

const a = [];
for (let i = 0; i < 5; ) {
  a.push(function () {
    console.log(i)
  });

  i++; // 条件改变放到循环体内
}
a[0](); 
a[1]();

不同之处也已标明:i++的条件改变位置换到了循环体内执行,这时预期结果也不一样了。
执行预期应该是输出0, 1,实际却输出的是 1, 2。

这是为什么呢?同样通过babel转换源码后探知:

var a = [];

var _loop = function _loop(_i) {
  a.push(function () {
    console.log(_i);
  });
  _i++; // 循环体内的条件改变影响到循环体函数内的局部变量

  i = _i;
};

for (var i = 0; i < 5;) {
  _loop(i);
}

a[0]();
a[1]();

最近也是在写一个比较复杂的业务逻辑时,发现这种写法的问题,好在花费了一些时间总算是搞清楚了。

如果循环体是即时执行是不会有问题的,如果是延迟执行的则会暴露该问题。

http://cssor.com/change-condition-in-for-loop.html

参与评论

电子邮件地址不会被公开。 必填项已用*标注

*

全部分类
Books(4)code(7)database(6)html&css(24)java(11)JavaScript(57)jQuery(24)linux(20)python(1)React(1)share(1)soft(4)solution(54)thinking(17)vim(9)WordPress(8)前端优化(12)拓展(33)服务器(33)移动开发(4)自然(22)