执行上下文与调用栈

在开始今天的内容之前,先看一段代码,下面的代码在同一个 js 文件中定义。代码中的打印结果是什么?

function log() {
   console.log(name);
}


function welcome() {
    var name = "前端小课";
    log();
}
var name = "素燕";
welcome();

我们一起来分析下:

当 JavaScript 代码被执行的时候,首先会创建一个「全局执行上下文」,你可以把执行上下文理解为一段代码要执行时需要准备的环境,它主要包含变量环境、词法环境、this 等。

代码执行时需要经历两个阶段:编译、执行。编译完成后需要创建全局执行上下文。var定义的变量和函数声明会被保存到变量环境中,let、const 定义的变量会保存到词法环境中。

在 var 很傻、let 很亲切 、const 更坚定 这节课程中我们有提到过变量提升,其实变量提升的原理就是把变量的声明提前注入到执行上下文中,上面的代码在执行之前,全局执行上下文如下图所示:

1. name 通过 var 声明,发生变量提升,保存到变量环境中;

2.函数 welcome 和 log 被添加到变量环境中,指向函数的实现,函数的实现会被保存到堆中,此时 welcome 和 log 其实是一个指向函数实现的指针;

可执行的代码,其实是:

name = "素燕";
welcome();

执行 name = “素燕”,给属性 name 赋值。全局执行上下文变为:

然后执行函数 welcome。执行函数的时候会创建被执行函数的上下文,并压入调用栈中。JavaScript 引擎通过调用栈来管理函数的执行,函数发生调用时把函数执行环境入栈,当函数返回的时候(执行结束)进行出栈操作。

执行代码为:

name = "前端小课";
log();

给 name 属性赋值,welcome 函数执行上下文为:

此时的调用栈如下图:

执行 log 函数,创建 log 函数的执行上下文,并压入堆栈,调用栈为:

JavaScript 代码使用的是词法作用域,它是一种静态作用域,和代码的书写有关系,与代码执行无关。也就是说作用域是一成不变的。

function log() {
    console.log(name);
}


function welcome() {
    var name = "前端小课";
    log();
}
var name = "素燕";
welcome();

上面这段代码会存在一个全局作用域,log 函数作用域和 welcome 函数作用域,JavaScript 代码执行的时候,会从当前作用域查找变量,如果未找到会到它的外层作用域中查找。log 函数的外层作用域是全局作用域,故 log 函数的打印值为全局作用域定义的变量。打印结果为“素燕”。

当 log 和 welcome 函数执行完后,它们的执行上下文会依次出栈,并释放它使用的内存空间。全局执行上下文的内存空间会随着页面的生命周期一直保留着。


推荐阅读:

字节码与二进制的“样貌”

从源码到抽象语法树可视化

用2000字详细解答昨天的题目(再忙也要看一下)

我的2019 —我与《前端小课》

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值