一.执行上下文和执行上下文栈
-
变量声明提升和函数声明提升
1) 变量声明提升
通过var定义(声明)的变量, 在定义语句之前就可以访问到
值: undefined
2) 函数声明提升
通过function声明的函数, 在之前就可以直接调用
值: 函数定义(对象)
-
全局上下文和函数上下文
1) 全局执行上下文
在执行全局代码前将window确定为全局执行上下文
对全局数据进行预处理
var定义的全局变量=>undefined, 添加为window的属性
function声明的全局函数=>赋值(fun), 添加为window的方法
this==>赋值(window)
开始执行全局代码
2) 函数执行上下文
在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象(虚拟的, 存在于栈中)
对局部数据进行预处理
形参变量=>赋值(实参)=>添加为执行上下文的属性
arguments==>赋值(实参列表), 添加为执行上下文的属性(实参列表)
var定义的局部变量=>undefined, 添加为执行上下文的属性
function声明的函数 =>赋值(fun), 添加为执行上下文的方法
this==>赋值(调用函数的对象)
开始执行函数体代码
-
执行上下文栈
1) 在全局代码执行前, JS引擎就会创建一个栈来存储管理所有的执行上下文对象
2) 在全局执行上下文(window)确定后, 将其添加到栈中(压栈)
3)在函数执行上下文创建后, 将其添加到栈中(压栈)
4)在当前函数执行完后,将栈顶的对象移除(出栈)
5) 当所有的代码执行完后, 栈中只剩下window
- 测试题
题一:
1)上题依次输出什么?
gb: undefined
fb: 1
fb: 2
fb: 3
fe: 3
fe: 2
fe: 1
ge: 1
2) 整个过程中产生了几个执行上下文? 5个(n+1 1为window对象,n是执行了几次函数)
题二: 先执行变量声明提升还是函数声明提升?
基于视频和查找资料了解有两种说法
1)第一种说法,先执行变量声明提升,然后再执行函数声明提升,由于发生同名,所以函数会覆盖掉变量声明,因此输出的结果为function。当变量声明时进行赋值的话,顺序就是变量提升然后函数提升,这个时候函数将变量覆盖,接着变量赋值的话,会将原本a中保存的函数对象地址设置为2,所以会显示number。
2)第二种说法,先执行函数声明提升,然后再执行变量声明提升,但是变量声明提升不会覆盖函数声明提升,所以显示function,但给a设置2时,后面逻辑和上面一样。
题三:
变量声明提升,var b在全局作用域中,(b in window)为true,所以没有进行赋值,输出为undefined
题四:
c=1为最后的结果,c(2)等同于1(2) 报错
二.作用域和作用域链
- 作用域:
1.)理解
它是静态的(相对于上下文对象), 在编写代码时就确定了
2.)分类
全局作用域
函数作用域
没有块作用域(ES6有了)
3) 作用
隔离变量,不同作用域下同名变量不会有冲突
- 作用域链:
1) 理解
多个上下级关系的作用域形成的链, 它的方向是从下向上的(从内到外)
查找变量时就是沿着作用域链来查找的
2) 查找一个变量的查找规则
在当前作用域下的执行上下文中查找对应的属性, 如果有直接返回, 否则进入2
在上一级作用域的执行上下文中查找对应的属性, 如果有直接返回, 否则进入3
再次执行2的相同操作, 直到全局作用域, 如果还找不到就抛出找不到的异常
- 测试题
题一:
输出结果为10,是因为在执行show函数时,将fn函数的地址值传递过去,执行fn函数,fn函数只是在show函数内部被执行了,但是fn函数的作用域还是不变的,在自己的作用域中找不到x,然后向上寻找,x=10。
题二: