这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战
前言
TIP 👉 JS执行上下文及相关调用栈层面的只是,已经是JS语言类考题里面非常身图和底层的考察点。
执行上下文是什么
执行上下文,从定义上理解,是“执行代码的环境”
执行上下文的分类
- 全局上下文 全局代码所处的环境,不再函数中国年的代码都在全局执行上下文中
- 函数上下文 在函数调用时创建的上下文
- Eval执行上下文 运行Eval函数中的代码时所创建的环境,eval被诟病多年,不讨论
1. 全局上下文的创建和组成
当我们的JS脚本跑起来之后,第一个被创建的执行上下文就是全局上下文。
当我们的脚本里一行代码页没有的时候,全局上下文只有两个东西:
- 全局对象(浏览器是window,Node环境是Global)
- this变量,这里的this,指向的还是全局
var name = ‘qinglian’
var tel = '18519999999'
function getName() {
return {
name: name
}
}
创建阶段全局上下文
2. 上下文生命周期
- 创建阶段 ----- 执行上下文的初始化状态,此时一行代码都没有执行
- 执行阶段 ----- 逐行之行脚本里的代码
创建阶段里
- 创建全局对象
- 创建this,指向全局
- 给变量和函数安排内存空间
- 默认给变量赋值undefined,将函数声明放入内存
- 创建作用域链
执行阶段该有值的都有值了,这是因为JS引擎已经在一行一行之行代码、之行赋值操作了。
执行上下文在执行阶段里歧视始终是处在一个动态,比如你执行完第一行没执行第二行的时候,这时候就只有name有值了,而tel还是undefined,再往下执行,tel就有值了
站在执行上下文的角度,理解“变量提升”的本质
其实根本不存在任何的“提升”,变量一直在原地,所谓的“提升”,只是变量的创建过程(上下文呢创建阶段)和赋值过程(上下文执行阶段)的不同步带来的一种错觉。
执行上下文在不同阶段完成的不同工作,才是“变量提升”的本质
函数上下文的创建和组成
函数上下文不会创建全局对象,而是创建参数对象(arguments):创建出的this不再死死指向全局对象,而是取决于该函数是如何被调用的----------如果它被一个引用对象调用,那么this就指向这个对象;否则,this的值会被设置为全局对象活着undefined
var name = ‘qinglian’
var tel = '18999999999'
function getNameTel() {
retunr {
name: name,
tel: tel
}
}
getNameTel()
当引擎执行到getNameTel调用这一行时,首先会进入函数上下文的创建阶段,
接着进入执行阶段,逐行执行函数内部的代码,此处我们只有一行代码,在代码执行过程中,没有涉及到变量的修改,因此函数上下文的内容保持不变,执行完毕后,函数上下文的生命周期就结束了。
调用栈
当函数执行完毕后,其对应的执行上下文也随之消失了,这个消失的过程,我们叫它“出栈”,在JS代码的执行过程中,引擎会为我们创建“执行上下文栈”也就是调用栈
function testA() {
console.log('执行第一个测试函数的逻辑')
testB();
console.log('再次执行第一个测试函数的逻辑')
}
function testB() {
console.log('执行第二个测试函数的逻辑')
}
testA()
执行之初,全局上下文创建:
执行到testA调用处,testA对应的函数上下文创建:
执行到testB调用处,testB对应的函数上下文创建:
testB执行完毕,对应上下文出栈,剩下testA和全局上下文:
testA执行完毕,对应执行上下文出栈,剩下全局上下文:
整个过程的调用栈:
站在调用栈的角度,理解作用域
作用域其实就是当前所处的执行上下文。
注意:沿着作用域链找,可不是沿着调用栈一层一层往上找,调用栈实在执行过程中形成的,而作用域链可是在书写阶段就决定的了