前言
在 Es6 之前有块之说,但没有块级作用域之说!!!如下:
if (true) {
var a = '1'
}
console.log(a) // 1
for (var i = 1; i < 3; i++) {
// TODO
}
console.log(i) // 3
如上:一个{}
即形成了一个块。但是没有作用域一说,故在外面也可以正常访问到var
声明的变量 。
我为什么说是 函数作用域(局部作用域) 而不是 局部作用域(函数作用域) 这样写,就是想特意说一下,在函数内部可以形成一个局部作用域!!!
接下来以代码形式演示各个作用域:
var a = 1
console.log(a) // 1
{
var a = 2
}
console.log(a) // 2
function test() {
var a = 3 // 函数作用域(局部作用域),只在函数内部可以访问
}
test()
console.log(a) // a is not defined
function test() {
a = 3 // 在函数内部声明一个全局作用域,在外部也可以访问
}
test()
console.log(a) // 3
如上:在 全局 或 块 里面定义一个变量,或在 函数里面 定义一个不带var的变量即定义了一个全局作用域的变量。即也说明了在函数作用域内的变量,在外面不能访问到。
事件点击循环
var element = [{}, {}, {}]
for (var i = 0; i < element.length; i++) {
element[i].onclick = function () {
console.log(i) // 注意:此时的i是上层作用域值,故不属于函数作用域内的变量!!!
}
}
element[1].onclick() // 3
因为{}块没有作用域的概念,所以 i
即是一个全局变量,而执行事件是在for循环之后执行的,此时i
已经变为了3,所有引用i
的地方都变为了3
以 函数作用域
的方式升级
for (var i = 0; i < element.length; i++) {
element[i].onclick = (function (i) { // 函数1
// 每次循环相当于位置在函数1中定义了一个新的变量,此时为函数2拿的值为当前作用域的即传入的值
return function () { // 函数2
console.log(i)
}
})(i)
}
element[1].onclick() // 1
此时把onclick写成一个闭包的形式,然后传入把i
传入,这样传入的i
为函数1中的变量,而js中,只有在函数内部的函数才能读取局部变量,而函数2中所读取的i
为函数1的局部变量。故在每次循环中,把i
保存了下来。故每次i
都不是3而是每次循环出来的传入的值。
以 let
升级
for (let i = 0; i < element.length; i++) {
element[i].onclick = function () {
console.log(i)
}
}
element[1].onclick() // 1
使用let
升级后,块 有了 块级作用域的概念。在定义i的时候,每个{} 花括号内形成一个独立作用域,与闭包类似。
提问:for循环是怎么形成块级作用域的
for (let i = 0; i < 3; i++) {
console.log(i);
}
console.log(i); // i is not defined。因为i在for形成的块级作用域内,故外部拿不到i
// 实际执行如下:
{
let i = 0 // for循环的作用域
if (i < 3) {
console.log(i); // if判断里面的作用域,此时的i在一个新的{}块内部,有自己的独立块级作用域。而使用var定义变量的话{}不 // 能形成块级作用域
}
i++
// TODO...
}
由上可明确的展示 for 循环中,var 和 let 的区别。
再来一问
for (let i = 0; i < 3; i++) {
let i = 'foo'
console.log(i); // foo
}
如上拆解循环
{
let i = 0 // for循环的作用域
if (i < 3) {
let i = 'foo'
console.log(i); // 此时在新的跨级作用域内重新定义了 i。所以不会报错。
}
i++
// TODO...
}