一、作用域
在JavaScript中,变量的作用域有全局作用域和局部作用域两种,局部作用域又称为函数作用域。
- 全局作用域
在代码中任何地方都能访问到的对象拥有全局作用域 - 局部作用域(函数作用域)
局部作用域在函数内创建,在函数内可访问,函数外不可访问
二、作用域链
了解作用域链之前我们要知道一下几个概念:
变量和函数的声明
函数的生命周期
Activetion Object(AO)、Variable Object(VO)
变量和函数的声明
在JavaScript引擎解析JavaScript代码的时候,首先,JavaScript引擎会把变量和函数的声明提前进行预解析,然后再去执行其他代码,也就是变量提升和函数提升。函数命名法实际上是变量提升,赋值内容还是在原位。
如果变量名和函数名声明时相同,函数优先声明
什么是AO、VO
英文解释:
AO:Activetion Object(活动对象)
VO:Variable Object(变量对象)
VO对应的是函数创建阶段,JS解析引擎进行预解析时,所有的变量和函数的声明,统称为Variable Object。该变量与执行上下文相关,知道自己的数据存储在哪里,并且知道如何访问。VO是一个与执行上下文相关的特殊对象,它存储着在上下文中声明的以下内容:
变量 (var, 变量声明);
函数声明 (FunctionDeclaration, 缩写为FD);
函数的形参
举个例子:
function add(a,b){
var sum = a + b;
function say(){
alert(sum);
}
return sum;
}
// sum,say,a,b 组合的对象就是VO,不过该对象的值基本上都是undefined
AO对应的是函数执行阶段,当函数被调用执行时,会建立一个执行上下文,该执行上下文包含了函数所需的所有变量,该变量共同组成了一个新的对象就是Activetion Object。该对象包含了:
函数的所有局部变量
函数的所有命名参数
函数的参数集合
函数的this指向
举个例子:
unction add(a,b){
var sum = a + b;
function say(){
alert(sum);
}
return sum;
}
add(4,5);
// 我用JS对象来表示AO
// AO = {
// this : window,
// arguments : [4,5],
// a : 4,
// b : 5,
// say : ,
// sum : undefined
// }
JavaScript作用域链
当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)来保证对执行环境有权访问的变量和函数的有序访问。作用域第一个对象始终是当前执行代码所在环境的变量对象(VO)
function add(a,b){
var sum = a + b;
return sum;
}
假设函数是在全局作用域中创建的,在函数a创建的时候,它的作用域链填入全局对象,全局对象中有所有全局变量,此时的全局变量就是VO。此时的作用域链就是:
此时作用域链(Scope Chain)只有一级,就为Global Object
scope(add) -> Global Object(VO)
VO = {
this : window,
add :
}
如果是函数执行阶段,那么将其activation object(AO)作为作用域链第一个对象,第二个对象是上级函数的执行上下文AO,下一个对象依次类推。
add(4,5);
例如,调用add后的作用域链是:
此时作用域链(Scope Chain)有两级,第一级为AO,然后Global Object(VO)
scope(add) -> AO -> VO
AO = {
this : window,
arguments : [4,5],
a : 4,
b : 5,
sum : undefined
}
VO = {
this : window,
add :
}
在函数运行过程中标识符的解析是沿着作用域链一级一级搜索的过程,从第一个对象开始,逐级向后回溯,直到找到同名标识符为止,找到后不再继续遍历,找不到就报错
因此我们在实际使用时可以从函数开始找起,逐级向上一层作用域寻找变量