前言
先出一个十分常见的题目, 如下:
var age = 99;
function t() {
console.log(age)
var age = 100
}
t()
很明显, 这个题目会打印出undefined。 那么为什么呢?
很多人都会说: 因为变量提升啊, 这个时候没有赋值啊, 所以是undefined。 说的对, 那么变量提升又是怎样产生的呢?
一个函数的执行过程
很多书上说javascript是解释性语言, 但是javascript其实在执行之前是有一个编译过程的——词法分析。
对于一个函数执行分为了两个部分:
1. 编译过程——词法分析
2. 执行过程
词法分析需要分析三个内容:
1. 分析函数参数
2. 分析变量声明
3. 分析函数声明
具体步骤:
- 函数运行前的一瞬间, 生成Active Object(活动对象), 一下简称AO
- 函数声明的参数, 形成AO的属性, 值为实参, 未传值为undefined
- 分析变量声明, 如var age,
1. 如果AO上还没有age属性, 则添加AO属性吗值为undefined
2. 如果AO上已经有age属性, 则不做任何影响- 分析函数声明, 如function foo(){}, 则把函数赋值给AO.foo属性; 如果foo存在则无情覆盖掉
具体分析最开时的代码
var age = 99;
function t() {
console.log(age)
var age = 100
}
t()
- 在调用f函数时, 形成一个AO
- 没有参数, 不做参数分析, 此时AO没有属性
- 分析变量age,将age属性添加到AO上,此时age的值为undefined
- 分析函数声明, 此时没有函数声明, 跳过
- 经过分析最终形成了一个AO={age:undefined}
- 进入执行过程
- 执行console.log(age), 此时为undefined, 所以打印undefined
- 执行age = 100
- 此时AO={age:100}
- 结束
以上就是一个函数的玩笑执行过程。 也可以理解变量提升真正的原因。
作用域链
其实可以看到, 当我们在函数内部使用一个变量时, 需要在AO对象上查找这个变量属性。 那么如果没有这个变量属性呢?这时怎么办?
按以往的经验,应该不难理解函数会到外层函数的AO上查找, 如此递归直到window对象,所以由这些AO所形成的一条链, 叫做作用域链