例题:
var a = 1;
function fn() {
var a
console.log('1.'+a);//undefined
a = 5;
console.log('2.'+a);//5
function fn2() {
console.log('3.'+a);//6
a = 20;
}
a++;
fn2();
console.log('4.'+a);//20
}
fn();
console.log('5.'+a);//1
例题中的作用域链:
//全局作用域下的变量对象
window.scope={
a://var a = 1的时候是1,a = 10的时候为10,
fn:function () {
...
}
}
//fn()作用域下的变量对象
1.fn.scope={
a://声明的时候是undefined,a = 5的时候是5,a++的时候是6,fn2()中a = 20赋值过后是20,
fn2:function () {
...
}
}
2.window.scope={
a:1,
fn:function () {
...
}
}
//fn2()作用域下的变量对象
1.fn2.scope={}
2.fn.scope={
a://声明的时候是undefined,a = 5的时候是5,a++的时候是6,fn2()中a = 20赋值过后是20
fn2:function () {
...
}
}
3.window.scope={
a:1,
fn:function () {
...
}
}
console.log('1.'+a)
:在JS中的语句var a = 1
其实是两条语句var a
和a = 1
的合并,其中语句var a
由于声明前置会被提到作用域的开头,例如:
console.log(a);
var a = 1;
这个console.log(a)
会输出undefined。
console.log('2.'+a)
:在fn()的作用域中,a已经被声明过,所以a = 5
就直接将5赋值给a。
console.log('3.'+a)
:为什么会输出6呢,因为:
- 在进入fn2()的执行环境之前,有一个
a++
- fn2()的作用域中没有声明a,在调用a这个变量的时候就会顺着作用域链往外层查找,在fn()的作用域中找到了已经被声明的a
console.log('4.'+a)
:不是说好的外层作用域不能访问内层吗,可是为什么fn2()中的a = 20
会影响到fn()里的a呢?因为:
- fn2()中的
a = 20
改变的本来就是fn()中声明过的a的值
console.log('5.'+a)
:在fn2()中a = 20
没有var关键字,不是说在函数内没有使用var关键字直接给变量赋值会成为全局变量吗,这里的console.log输出不应该是20吗,为什么还是1呢?
-
首先我们看看为什么函数内没有加var关键字的变量会变成全局变量呢?
例如:function fn() { b = 1; } fn();
我们都知道,在全局作用域下的变量和函数都是window对象的属性和方法。其实在fn()被调用的时候,
b = 1
顺着作用域链想找到被声明的b赋值,但是无论fn()的作用域还是全局作用域都没声明b,所以解析器就做了window.b=1
这样一个行为,就是给window对象加了一个b:1
的名值对。 然后为什么fn2()的
a = 20
没有加到全局对象上呢?
因为fn2()顺着作用域链查找的时候在fn()的作用域中找到了已经被声明过的a,所以20就直接赋值给了fn()中已经声明过的a。