这篇只是简单的做下记录吧,作用域在 js 中真是个麻烦的东西,实在有太多的 case by case 的情况,无法用一个原理推出其他的,先来代码:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var lis = document.querySelectorAll( 'li' );
// 用 var 声明循环变量
for( var i = 0; i < lis.length; i++ ) {
lis[ i ].onclick = function() {
alert( i ); // 点击任何一个 li 都是 alert 3
}
}
// 用 let 声明循环变量
for( let j = 0; j < lis.length; j++ ) {
lis[ j ].onclick = function() {
alert( j ); // 点击 li 分别显示 0,1,2
}
}
</script>
用 var 声明的变量不是块级的,所以整个循环过程中 i 变量只有一个,当你注册完 onclick 之后再点击,所有函数引用的是同一个 i,所以 alert 出来的是 i 的最终值 3。而如果用 let 声明为什么会产生不同的效果?
从常识推断:j 虽然是块级作用域变量,但是仅仅能说明在 for 循环外面拿不到 j。并且 for 循环第一个分号前的代码只会执行一次,即使如果每次循环 j 都是生成一个新的局部变量,那么 j++ 为什么能保持 j 数字的递增呢?
好吧,只能再去撸下规范。原来规范里面针对这种情况是这么规定的(我依然只是说明下大致的步骤):
- 每次循环都会新建一个块级作用域;
- 相应的 for 循环第一个分号前所有用 let 声明的局部变量都会重新新建。
- 然后把上一次循环后变量的值赋值给这次循环生成的新局部变量(当然是找同名的啦)。
所以就能解释上面代码用 let 声明的情况了,再写一个示例代码来验证下规范的逻辑:
for ( let a = 0, b = 10; a < 5; a++, b++ ) {
console.log( 'a:' + a + ' | b:' + b );
}
/*
结果:
a:0 | b:10
a:1 | b:11
a:2 | b:12
a:3 | b:13
a:4 | b:14
*/
for ( let c = 0, d = 10; c < 5; ) {
console.log( 'c:' + c + ' | d:' + d );
c++;
d++;
}
/*
结果:
c:0 | d:10
c:1 | d:11
c:2 | d:12
c:3 | d:13
c:4 | d:14
*/
网友评论