1.JavaScript作用域与作用域链

<1>声明提前:

JavaScript函数里声明的所有变量都被提前至函数体的顶部。示例:

1
2
3
4
5
6
7
8
9
var scope="global";
function fun(){
console.log(scope);
var scope="local"
console.log(scope);
}
//answer
undefined
local

由于声明提前,fun()内的变量scope的声明相当于被提前到fun()的最前面,但此时没有被初始化,所以第一个log打印出的是“undefined”。

 

<2>全局对象属性:

JavaScript中,没有用var声明的变量都是全局变量,而且是顶层对象的属性。示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
function fun(){
if(true){
s="ifscope";
for(var i=0;i<2;i++);
}
console.log(i);
}
fun();
console.log(s);
//answer1
2
ifscope

虽然s是在fun()中定义的,但离开了fun()的作用域s仍然可用,因为s是顶层对象的属性。

 

<3>作用域链

作用域链是一个对象列表或者链表,这组对象定义了这段代码“作用域中”的变量,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function a(){
var name = "a";
function b(){
var name = "b";
console.log(name);
}
function c(){
var name = "c";
console.log(name);
}
b();
c();
console.log(name);
}
a();
//answer
b
c
a

在运行b()时,作用域链是b()->a()->全局

 

2.with语句

with语句将对象添加到作用域链的头部,然后执行一个代码块,离开代码块时作用域恢复到原来的状态。

在对象嵌套层次很深的时候通常会使用with语句类简化代码编写,例如:

1
2
3
document.forms[0].name.value = "";
document.forms[0].address.value = "";
document.forms[0].email.value = "";

可以写成:

1
2
3
4
5
with(document.forms[0]){
name.value = "";
address.value = "";
email.value = "";
}

这种方法减少了大量的输入。当然,不适用with语句的等价代码也可以是:

1
2
3
4
var f = document.forms[0];
f.name.value = "";
f.address.value = "";
f.email.value = "";

 

with语句注意点:

若尝试将obj添加到作用域链头部并创建新的属性:

1
with(obj) x = 1;

若对象obj有一个属性x,那么这行代码给这个属性赋值为1,若obj中没有属性x,这段代码和不适用with语句的代码x=1是一模一样的。可见,with语句提供了一种读取obj属性的快捷方式,但不能创建obj的属性。