作用域提升
提升演示
var a = 2; JavaScript 实际上会将其看作两个声明:var a; 和 a = 2; 第一个定义声明是在编译阶段进行的。第二个赋值声明会被留在原地等待执行阶段。
var a = 2;
// 看作如下
var a;
a = 2;
console.log( a );
变量和函数声明从它们代码中出现的位置被“移动”到了最上面(提升到当前作用域的最前面!),这个过程叫做提升。
函数优先
函数声明和变量声明都会被提升,但函数会首先提升,然后才是变量。
初始代码
foo() // 3
function foo() {
console.log(1)
}
var foo = function() {
console.log(2)
}
function foo() {
console.log( 3 )
}
提升后的代码
function foo() {
console.log(1)
}
function foo() {
console.log(3)
}
var foo
foo()
foo = function () {
console.log(2)
}
变量的全局作用域
全局作用域在文件任何地方都可以访问到,对应的变量叫“全局变量”。全局变量一般定义方式:
- 不被任何函数包裹的变量;
window对象的属性(无显式声明的变量,会被设置为window对象的属性;在顶级作用域使用var声明的变量,会被设置为window对象的属性)
变量的局部作用域
局部作用域一般只在固定的代码片段内可以访问到,对应的变量叫“局部变量”,最常见的例如函数内部,所以也称为“函数作用域”;多个函数嵌套,那么这个作用域会形成“作用域链” 。
- 访问限制:内部可以访问外部,外部不能访问内部;
- 访问优先级:从内到外,当前作用域优先。
function box(){
var num = 123
}
box()
console.log(num) // error num is not defined
函数的作用域
每个函数都会创建自己的作用域,作用域在函数定义时就已经确定,而不是在函数调用时。 函数是ES5中除了全局作用域,唯一有作用域的地方。
var x = 10
function fn() {
console.log(x)
}
function show(f) {
var x = 20
f()
}
show(fn) // 10 作用域在函数定义时就已经确定
无块级作用域导致的问题
变量提升,导致内层变量覆盖外层变量。
var num1 = 10
function fn1(){
console.log(num1)
var num1 = 20
}
fn1()
// 变量提升后代码实际变成这样:
var num1
num1 = 10
function fn1(){
var num1
console.log(num1)
num1 = 20
}
fn1() // undefined
循环计数的变量,泄露为全局变量。
var arr = [1,2,3,4,5]
for( var i = 0; i < arr.length; i++ ) {
console.log('内部' + i)
// 内部0 内部1 内部2 内部3 内部4
}
console.log('外部' + i) // 外部5
// 变量提升后
// 因为 {} 没有作用域,所以变量声明提升后 i 成了一个全局变量,所有 i 最后结果是 5
var i
for( i = 0; i < arr.length; i++ ) {
console.log('内部' + i)
}
console.log('外部' + i) // 外部5
具体解释请见 《深入理解ES6》第一章 循环中的块级绑定