本来想要在“作用域”这个专题上自己总结出一些东西的,结果想了好久都没有形成一个固定的思路,也不想贸然拷贝网上的说法。所以,还是先记录几个容易犯错的小例子,以后再来形成总结吧。
1、“变量声明提升”
例子1:
var x = 'global';
function f1() {
console.log(x);
}
function f2() {
console.log(x);
/*
这里x的声明让f2的函数作用域中拥有了x这个变量,所以以后访问x的时候,不会再沿着作用域链上搜索
而上面那一句把x打印出来的时候,x只是声明了但没有初始化
所以输出undefined
*/
var x = 'local';
}
f1(); // global
f2(); // undefined
例子2(摘自360校招笔试题):
var a, b;
(function () {
console.log(a);// undefined
console.log(b);// undefined
var a = b = 0;// (1)
console.log(a);// 0
console.log(b);// 0
})();
console.log('window', a);// window undefined
console.log('window', b);// window 0
解释:这里最关键的是理解清楚语句(1)处的执行情况,这个语句做的工作实际上是:
b = 0; // (2)
var a = b; // (3)
语句(2)执行之后,在匿名函数的scope中并没有找到b的声明,故这里修改的是全局scope中的b,也就是让window.b赋值为0了。所以,在语句(1)之前,window.b的值是undefined,在(1)之后,window.b就是0了,就算出了匿名函数,window.b也是0。
语句(3)声明了a,那么匿名函数中所有出现的a都是本scope中的a,而不是window.a。所以,在a声明了却还没初始化的时候,a的值为undefined,初始化之后就是0了。而出了匿名函数之后,打印的是window.a的值,自然是undefined了。
2、js的函数作用域是静态作用域
var x = 'global';
function f1() {
console.log(x);
// 此函数被调用的时候,x在这里没有声明,所以会沿着作用域链开始寻找
// 然而,Js的作用域是“静态”作用域,也就是说在声明函数的时候就可以确定而不必等到运行时
// 所以,这里的作用域链为:f1-->window,所以输出window.x的值“global”
}
function f2() {
var x = 'f2';
// f2在函数内部调用f1
f1();
}
f1(); // global
f2(); // global(并非f2)
如果把作用域的嵌套结构改成下面的样子,结果就会不一样:
var x = 'global';
function f1() {
console.log(x);
}
function f2() {
var x = 'f2'; // (1)
// 将函数f1声明在f2内部,这样,作用域链就变成:
// f1 --> f2 --> window
// 所以输出的是f2的x即'f2'
function f1() {
console.log(x);
}
f1();
}
f1(); // global
f2(); // f2
举一反三,如果把上面(1)这一句放在f1被调用的后面,自然就输出“undefined”了:
var x = 'global';
function f1() {
console.log(x);
}
function f2() {
//var x = 'f2'; // (1)
// 将函数f1声明在f2内部,这样,作用域链就变成:
// f1 --> f2 --> window
// 所以输出的是f2的x即'f2'
function f1() {
console.log(x);
}
f1();
var x = 'f2'; // (1)
}
f1(); // global
f2(); // undefined
3、不加var修饰的变量,并不一定是window下的变量
例如下面这个小程序,很容易以为输出的是“global”,然而并不是:
function f1() {
function f2() {
x = 'global'; // (2)
// 解释:
// 这里x没有var关键字修饰,但也不一定就是指window.x
// 根据作用域链:f2 --> f1 --> window
// 首先,在f2中,并没有x的声明,所以去f1找
// 然后,在f1中,语句(1)声明了有x,所以在这一层停止搜索x了
// 也就是说,语句(2)修改的是f1函数作用域中的x
// 所以最终,输出window.x的值是undefined
}
f2();
var x = 10; // (1)
}
f1();
console.log(window.x); // 结果:undefined