执行期上下文(AO):函数在运行时(或预编译时),会创建一个叫执行期上下文问的一个内部对象。AO或GO,他定义了一个函数的执行环境(用来做变量/函数的提升等等),函数执行完毕则会销毁。
作用域:每一个函数对象都有一个[[scope]]属性,他内部存储了执行期上下文的集合,也就是我们说的作用域。这个集合是一种链式结构,叫做作用域链
作用域链的形成过程
当函数被定义时,这个函数的[[scope]]属性会指向它的上一级函数的[[scope]]属性的值的引用
但函数被执行时,这个函数的[[scope]]属性会在他的最上层添加他自己的执行期上下文(AO)。
当函数执行完成后,他自己的执行期上下文(AO)会被销毁
当查找某个变量或方法时,会从[[scope]]属性的最上层一直向下寻找。即先找它自己的AO,若未找到,则再向他的上一级函数的寻找
举个栗子:
function a(){
function b(){
var b = 123
}
var a = 10
b()
}
var glob = 100
a()
1.a函数定义时,它的[[scope]]会指向它的上一级即全局的执行期上下文(GO)
2.a函数执行时,他的[[scope]]会在他的最上层(最首位)添加自己的执行期上下文(AO),其祖先级依次下移(后移)。
3.此时,b函数被定义,它的scope属性指向它的上一级,即a的scope的引用。
4.b函数被执行时,会形成自己的AO,并置于其作用域链的最顶端(首部),其余依次下移(后移)。
5.此时,当某个函数内使用某个变量时,会在其作用域链上(scope chain)依次寻找.
注:一个函数得AO对象上,还包括this、arguments等属性,此处不一一介绍
作用域的分类
全局作用域:全局作用域指的是在全局起作用的作用域。一般指上面所说的GO
局部作用域(函数作用域):局部作用域指的是在局部起作用的作用域。一般函数的作用域都是局部作用域,即上面的AO
块作用域:一般而言,块作用域指的是以{}包含的内容快,函数或变量只在它所在的的快内起作用。es5中不存在块作用域,es6新增了块作用域。for循环和if语句的{}属于块作用域
举个例子
当我们使用if语句时,在if语句的内部定义了变量
var foo = true
if(foo){
var c = 2
console.log(c) // 2
}
console.log(c) // 2
变量c只在if所在的代码块中使用,因此将他声明在if语句内部是很必要的,但是通过var声明的变量最终都会属于外部作用域。相当于以下代码
var foo = true
var c
if(foo){
c = 2
console.log(c) // 2
}
console.log(c) // 2
所以就需要一些其他的结构或语法来实现块级作用域。在es6中新增了let/const,用来声明变量
let会把变量绑定在它所在的块中。
const用来声明常量,也会形成块级作用域。
var foo = true
if(foo){
let c = 2
console.log(c)
}
console.log(c) // 报错
由于let会形成块级作用域,所以if(){}外接受不到c变量,所以会报错