执行上下文
首先要知道js执行到可执行语句时,就会创建一个上下文,等到这个上下文执行上下文执行完毕,就会出栈,继续执行下面的代码,也有可能进入其他的上下文。
那么创建上下文有什么用呢?
用于确认变量是否可访问,确定this指向,以及变量的赋值。
创建上下文有两个阶段:
创建阶段:
该创建属于变量声明阶段,创建变量对象AO
function func(a,b){
console.log(c)
console.log(d)
var c=10
function d(){
}
}
func(1,2)
//伪代码
AO[func]={
a:1,
b:1,
c:undefined,
d:function
}
创建阶段其实就是变量声明的过程。
执行阶段:
执行代码阶段。这个阶端AO转换成VO,以及会新增arguments对象。this开始会指向一个明确的对象。
拿上面那个例子举例:其实很多小伙伴以前会觉得console.log(c)
这一条语句为什么会答应出undefined这样的结果,而不是c is not defined? 然后网上一查,结果大多都是变量提升。
当你深入执行上下文之后 ,你就会知道,在执行console.log(c)
之前有上下文创建阶段啊。AO中已经声明了a,只不过创建阶段不会赋值而已,所以就是undefined。
小伙伴又会问,为什么打印d的时候是函数体呢?
其实函数是js第一公民,声明函数是不用赋值语句的,在创建阶段就会把函数体指向d这个属性。
作用域链(scope)
在了解作用域之前,有必要先了解一下静态作用域这个概念,因为js是静态作用域的。
静态作用域:作用域是函数定义时就确定了的。
所以作用域链是在上下文创建阶段就确定了的。
一个函数的作用域链是自身活动变量加上级的活动变量们的集合。
闭包
可以访问到自由变量的函数就是闭包。
自由变量:不存在在自身上下文VO中的变量
其实根据定理可以看出js中所有的函数都可以是闭包,因为js中每个函数都有访问上级作用域的确定。
但一个函数能否访问另一个函数中的变量呢?问题变得复杂了起来。
本来函数执行完毕就会出栈,另一个函数根本不可能访问已出栈函数中的变量。
假设:已出栈函数为A,另一个函数为B。
我们能否把A的上下文环境的引用给B呢,这样就愉快解决问题了呢~
function clourse(){
var h=10
return function(){
h++
console.log(h)
}
}
let a=clourse()
a() //11
a() //12
clourse函数为A,A的返回值为B,当A执行完毕,返回值B赋值给了变量a。这样是不是变量a就拥有了A的上下文环境的VO。
于是函数a就能愉快的访问变量h了呢。
神奇的JS!