首先js中是没有“块状作用域”的,请看下面java例子
1
2
3
4
5
6
7
8
|
//scope1 作用域1
for
(
int
i =
0
; i <
10
; i++){
//scope2 作用域2
String str =
"123"
;
}
Systen.out.print(i);
Systen.out.print(str);
|
在有块状作用域的语言中,scope1与scope2是不同的作用域,所以
Systen.out.print(i);与Systen.out.print(str);都应该报错,i,str未定义
但js并没有块状作用域,看以下例子
1
2
3
4
5
6
7
8
|
//scope1 作用域1
for
(
var
i = 0; i < 10; i++){
//scope2 作用域2
var
str =
"123"
;
}
alert(i);
alert(str);
|
在js中scope1与scope2是同级的,所以alert(i)打印出10, alert(str)打印出123
不单for是这种情况,if,while等都是一样的,
上面例子等同于
1
2
3
4
5
6
7
8
9
10
|
//scope1 作用域1
var
i = 0;
var
str;
for
(; i < 10; i++){
//scope2 作用域2
str =
"123"
;
}
alert(i);
alert(str);
|
所以
1
2
3
4
5
6
7
8
9
10
|
//scope1 作用域1
var
els = document.getElementsByTagName(
"li"
);
for
(
var
i = 0, j = els.length; i < j; i++){
//scope2 作用域2
var
el = els[i];
el.onclick =
function
(){
//scope3 作用域3
alert(i);
}
}
|
其实等同于
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//scope1 作用域1
var
els = document.getElementsByTagName(
"li"
);
var
i = 0, j = els.length;
for
(; i < j; i++){
//scope2 作用域2
var
el = els[i];
el.onclick = fn;
}
function
fn(){
//scope3 作用域3
alert(i);
//当执行onclick时,i已经循环完毕
}
|
这时大家会想,scope3与scope1,scope2是不同一个作用域,为什么还能打印出来,
这是因为js有作用域链这种思想,当i在scope3找不到的时候,会向上级作用域scope1访问,如果类推,
直到scope1访问到,所以这里的i其实就是scope1中的i,这时i已经是循环完毕了。
因此要避免这种情况,我们就要从它的作用域入手了,请看改造例子
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//scope1 作用域1
var
els = document.getElementsByTagName(
"li"
);
for
(
var
i = 0, j = els.length; i < j; i++){
//scope2 作用域2
var
el = els[i];
el.onclick =
function
(i){
//scope3 作用域3
return
function
(){
//scope4 作用域4
alert(i)
}
}(i)
}
|
因为作用域链,alert(i)访问scope4没有,向上级scope3访问,它发现i已经由(i)传进来赋值了,
所以alert(i)这里的i其实是scope3作用域中的i,而并不是scope1中的i
而如果我们改成这样
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//scope1 作用域1
var
els = document.getElementsByTagName(
"li"
);
for
(
var
i = 0, j = els.length; i < j; i++){
//scope2 作用域2
var
el = els[i];
el.onclick =
function
(){
//这里没有声明形参
//scope3 作用域3
return
function
(){
//scope4 作用域4
alert(i)
}
}(i)
}
|
alert(i)中的i访问到的就是scope1的i,打印出来的还是循环完毕后的i...