For 循环作用域问题
for 循环简介
for
是一种前测试循环语句,但它具有在执行循环之前初始化变量和定义循环后要执行代码的能力,for
语句对常用的循环模式做了一些简化,大部分的循环都具有特定的计数器变量,循环开始之前要初始化这个变量,然后每次循环都要检测一下他的值,最后计数器变量做自增操作,在这一类循环中,计数器的三个关键操作是初始化、检测和更新。for
语句就将这三步操作明确声明为循环语法的一部分,各自使用一个表达式来表示
for(initialize; test; increment) {
/*
initialize: 初始化操作
test: 循环条件判断
increment: 计数器变量的更新
*/
}
将
initialize; test; increment
放在循环的第一行会更容易理解 for 循环正在做什么,而且也可以防止忘记初始化或者递增计数器变量
initialize
表达式只在循环开始前执行一次,初始化表达式应当具有副作用(通常是一个赋值语句)- 每次循环执行之前都会执行
test
表达式,并判断表达式的结果来决定是否执行循环体 - 如果
test
执行结果为真值,则最后执行increment
表达式,他也须要具有副作用
2. for 循环的作用域问题
- for 循环的
initialize
初始化操作部分使用var
关键字来声明变量时:
var arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = function () {
console.log(i);
}
}
console.log(i); // 10
arr[6](); // 10
var
声明的变量不存在块级作用域,因此在循环内部定义的变量也可以在外部访问- 在 javascript 中 for 循环并不是一个函数体,不存在相应的函数作用域,在 for 循环中定义的变量所处的作用域就是 for 循环所在作用域,也就是说通过
var i = 0;
声明的变量i
所在的作用域与arr
同一个级别,每次循环结束时i++
作用的也是同一个i
。 - 执行
arr[6]()
时,函数需要用到变量i
, 首先会在函数定义的上下文中查找变量i
,同样由于 for 循环不是函数体,没有相应的函数作用域,所以 查找到的i
所在的作用域还是与arr
同一级别的那个,这个时候i
经过循环多次的更新,已经是最终的结果10
- for 循环的
initialize
初始化操作部分使用let
关键字来声明变量时:
let arr = [];
for (let i = 0; i < 10; i++) {
arr[i] = function () {
console.log(i);
}
}
// console.log(i); // Uncaught ReferenceError: i is not defined
arr[6](); // 6
let
是 es6 新增的命令,用来声明变量,用法类似var
, 但是所声明的变量只在let
命令所在的代码块内有效,for
循环括号内声明的 变量只在循环内有效,因此循环外部不能访问- for 循环中如果用
initialize
部分使用let
初始化,那么这个for
循环中,设置循环变量的部分(for 后边的()
内)是一个父作用域,循环体(for 后边{}
内)内部是一个子作用域。 - 每次循环 javascript 都会为循环体创建独立的执行上下文,而且本轮循环所对应的
i
值,都是通过 javascript 引擎将记录的上一轮循环的i
值计算后得出的,但是变量i
是用let
声明的,只在当前轮循环有效 - 执行
arr[6]()
时,函数需要用到变量i
, 首先会在函数定义的上下文中查找变量i
,也就是函数定义所在的块级作用域中 变量i
的值。