作用域(Scoping)
在ES6之前,JavaScript是没有块级作用域的,只有全局作用域和函数作用域,var指令实际上是全局声明,但可以被函数作用域限制;
var a = 1; // globally scoped
// b is not visible here
function fn() {
// a is visible here
var b = 2; // function block scoped
}
提升(Hoisting)
在js中,声明变量和直接声明函数,会发生提升现象,声明会被提到当前作用域最高处;
变量提升
这是一个很普通的函数,alert全局变量str
;
var str = 'Hello World';
(function() {
alert(str); // Hello World
})();
如果在alert后面var
一个同名的局部变量,居然不能正常alert了,这就看起来很诡异了,因为我们知道,js的代码是由上至下运行的,就算是在这个函数作用域内部的局部变量str
覆盖了全局变量,也应该是先弹出Hello World
才符合常识;
就算是覆盖掉了,并且弹出这个局部变量,也应该是弹出test
而不是undefined
;
var str = 'Hello World';
(function() {
alert(str); // undefined
var str = 'test';
})();
这就牵扯到了js的变量提升
在声明变量时,声明会被提升到当前作用域的顶部,但变量值不会被提升。
于是上面的代码预编译(Precompile)后成了这样:
var str = 'Hello World';
(function() {
var str; // 声明被提升到了函数作用域顶部,在域内覆盖了全局变量,但此时没有被赋值
alert(str); // undefined
str = 'test';
})();
来个更直观的栗子:
// 未编译 // 预编译后
(function() { (function() {
alert(a+b+c); ===> var a,b,c;
var a = 1; ===> alert(a+b+c); // undefined
var b = 2; ===> a = 1;
var c = 3; b = 2;
})(); c = 3;
})();
函数提升
- 类似于变量提升,js中函数声明也会提升(Hoisting);
- 不同的是,函数提升会将整个函数声明(包括函数体)提升到当前作用域的顶部,这可以使我们先使用函数,后声明函数。
- 函数提升的优先级高于变量
fn(); // bingo
function fn() {
alert('bingo');
};
但将声明的函数存储到一个变量中是不会被提升的(变量提升不会提升变量值):
alert(typeof fn2); // undefined
fn2(); // typeError: fn is not a function
var fn2 = function fn2() {
alert('msg');
};