作用域

86 阅读2分钟

作用域提升

提升演示

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)
}

变量的全局作用域

全局作用域在文件任何地方都可以访问到,对应的变量叫“全局变量”。全局变量一般定义方式:

  1. 不被任何函数包裹的变量;
  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》第一章 循环中的块级绑定