js基础梳理-究竟什么是执行上下文栈(执行栈),执行上下文(可执行代码)?
日常在群里讨论一些概念性的问题,比如变量提升,作用域和闭包相关问题的时候,经常会听一些大佬们给别人解释的时候说执行上下文,调用上下文巴拉巴拉,总有点似懂非懂,不明觉厉的感觉。今天,就对这两个概念梳理一下,加深对js基础核心的理解。
1. 执行上下文(execution context)与可执行代码(execution code)
1.1 首先说一下,可执行代码的类型有哪些:
- 全局代码:例如加载外部的js文件或者本地标签内的代码。全局代码不包括 function 体内的代码
- 函数代码:function体内的代码
- eval代码:eval()函数计算某个字符串,并执行其中的js代码。比如eval("alert('hello world')")。虽然很强大,但实际用得很少,不讨论。
当js引擎遇到这三种类型的代码的时候,都会进行一些准备工作,这些准备工作,专业的说法就叫执行上下文。或者说js引擎遇到这三种类型的代码的时候,就会进入到一个执行上下文。
简而言之,执行上下文是评估和执行javascript代码的环境的抽象概念。每当javascript代码在运行的时候,它都是在执行上下文中运行。执行上下文可以理解为当前代码的执行环境,它会形成一个作用域(╭(╯^╰)╮,作用域就作用域嘛,说得这么拗口,非要搞个什么执行上下文的概念)。
1.2 那么js引擎在遇到可执行代码的时候,它究竟会做哪些准备工作呢?
- 全局执行上下文:创建一个全局的window对象(浏览器的情况下),并且设置 this 的值等于这个全局对象。一个程序中只会有一个全局执行上下文。
- 函数执行上下文:每当一个函数被调用时,都会为该函数创建一个新的上下文。每个函数都有自己的执行上下文,不过是在函数被调用时创建的。函数上下文可以有任意多个。每当一个新的执行上下文被创建,它会按定义的顺序执行一系列步骤。
- eval函数执行上下文:略
其实没必要刻意去区分可执行代码与执行上下文。个人理解,当别人跟你聊一些概念性的东西,聊到可执行代码,可执行上下文,执行环境的时候,其实他们可能是想说作用域,只不过表述方式不同罢了。
之前写的这个,有点问题,惭愧。作用域与执行上下文是完全不同的两个概念。
JavaScript代码的整个执行过程,分为两个阶段,代码编译阶段与代码执行阶段。编译阶段由编译器完成,将代码编译成可执行代码,这个阶段作用域规则会确定。执行阶段由引擎完成,主要任务是执行可执行代码,执行上下文在这个阶段创建。
2. 执行上下文栈(Execution context stack, ECS)
在一个javascript程序中,必定会产生多个执行上下文,javascript引擎会以栈的方式来处理它们,也就是执行上下文栈(很多文章可能会称它为执行栈,执行上下文堆栈,函数调用栈,其实都是差不多的意思)。
关于栈的概念和特性在上一篇博客:js基础梳理-内存空间已有介绍。
为了模拟执行上下文栈的行为,可以把它定义为一个数组:
ECStack = [];
现在 javascript遇到下面这段代码了
let a = 'hello world';
function first () {
console.log('进入 first 函数执行上下文');
second();
console.log('再次进入 first 函数执行上下文');
}
function second () {
console.log('进入 second 函数执行上下文');
}
first();
console.log('进入 全局执行上下文(Global Execution Context)')
当上述代码在浏览器加载时,Javascipt引擎创建了一个全局执行上下文并把它压入了执行上下文栈,用 globalContext表示它,并且只有当整个应用程序结束的时候(浏览器关闭),ECStack才会被清空,所以程序结束之前,ECStack最底部永远有个 globalContext:
ECStack = [
globalContext
];
当执行到一个函数的时候,就会创建一个执行上下文,并且压入执行上下文栈,当函数执行完毕的时候,就会将函数的执行上下文从栈中弹出。知道了这样的工作原理,就可以分析出 ECStack的变化过程:
// 伪代码
// first()
ECStack.push(<first> functionContext);
// first中调用了second,继续创建second的执行上下文
ECStack.push(<second> functionContext);
// second执行完毕
ECStack.pop();
// first执行完毕
ECStack.pop();
// javascript接着执行下面的代码,但是ECStack底层永远有个globalContext;
注意:函数中,遇到return能终止可执行代码的执行,因此会直接将当前上下文弹出栈。
例如,看以下这个闭包例子:
function f1(){
var n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
因为f1中的函数f2在f1的可执行代码中,并没有被调用执行,因此执行f1时,f2不会创建新的上下文,而直到result执行时,才创建了一个新的。具体演变过程如下:
// 伪代码:
// 全局上下文入栈:
ECStack = [
globalContext
];
// f1 EC入栈:
ECStack.push(<f1> functionContext);
// f1 EC出栈:
ECStack.pop();
// result EC入栈:
ECStack.push(<result> functionContext);
// result EC出栈:
ECStack.pop();
3.执行上下文的生命周期
3.1 创建阶段
- 生成变量对象(Variable object, VO)
- 建立作用域链(Scope chain)
- 确定this指向
3.2 执行阶段
- 变量赋值
- 函数引用
- 执行其他代码
在接下来的文章中将梳理创建阶段的这三个步骤。
js基础梳理-究竟什么是执行上下文栈(执行栈),执行上下文(可执行代码)?的更多相关文章
- js基础梳理-究竟什么是变量对象,什么是活动对象?
首先,回顾下上篇博文中js基础梳理-究竟什么是执行上下文栈(执行栈),执行上下文(可执行代码)?的执行上下文的生命周期: 3.执行上下文的生命周期 3.1 创建阶段 生成变量对象(Variable o ...
- js基础梳理-关于this常见指向问题的分析
首先,依然回顾<js基础梳理-究竟什么是执行上下文栈(执行栈),执行上下文(可执行代码)?>中的 3.执行上下文的生命周期 3.1 创建阶段 生成变量对象(Variable object, ...
- js基础梳理-如何理解作用域和作用域链?
本文重点是要梳理执行上下文的生命周期中的建立作用域链,在此之前,先回顾下关于作用域的一些知识. 1.什么是作用域(scope)? 在<JavaScritp高级程序设计>中并没有找到确切的关 ...
- js基础梳理-内存空间
我估计有很多像我这样非计算机专业的人进入到前端之后,总是在写业务代码,思考什么什么效果如何实现,导致很多基础概念型的东西都理解得并不太清楚.经常一碰到群里讨论的些笔试题什么的,总觉得自己像是一个假前端 ...
- js异步梳理:1.从浏览器的多进程到JS的单线程,理解JS运行机制
大家很早就知道JS是一门单线程的语言.但是也时不时的会看到进程这个词.首先简单区分下线程和进程的概念 1. 简单理解进程 - 进程是一个工厂,工厂有它的独立资源 - 工厂之间相互独立 - 线程是工厂中 ...
- js执行上下文栈和变量对象
JavaScript执行上下文栈和变量对象 JS是单线程的语言,执行顺序肯定是顺序执行,但是JS 引擎并不是一行一行地分析和执行程序,而是一段一段地分析执行,会先进行编译阶段然后才是执行阶段. 例子一 ...
- 一文弄懂js的执行上下文与执行上下文栈
目录 执行上下文与执行上下文栈 变量提升与函数提升 变量提升 函数提升 变量提升与函数提升的优先级 变量提升的一道题目引出var关键字与let关键字各自的特性 执行上下文 全局执行上下文 函数(局部) ...
- js执行上下文与执行上下文栈
一.什么是执行上下文 简单说就是代码运行时的执行环境,必须是在函数调用的时候才会产生,如果不调用就不会产生这个执行上下文.在这个环境中,所有变量会被事先提出来(变量提升),有的直接赋值,有的为默认值 ...
- 【进阶1-2期】JavaScript深入之执行上下文栈和变量对象(转)
这是我在公众号(高级前端进阶)看到的文章,现在做笔记 https://mp.weixin.qq.com/s/hZIpnkKqdQgQnK1BcrH6Nw 阅读笔记 JS是单线程的语言,执行顺序肯定是顺 ...
随机推荐
- TCP3次握手和4次挥手及其为什么
TCP 3次握手 客户端向服务器发送一个SYN(包含了SYN,SEQ). 当服务器接收到客户端发过来的SYN时,会向客户端发送一个SYN+ACK的数据包,其实ACK的ack等于上一次发送SYN数据包的 ...
- tidb 架构~tidb 理论学习(1)
一 简介:介绍新型NEW SQL数据库tidb 二 目的: tidb出现的目的,就是代替mysql+中间件,实现横向水平扩展 三 核心理论观点 1 MySQL 是单机数据库,只能通过 XA 来满足跨数 ...
- DES加密模式详解
DES加密模式详解 http://www.cnblogs.com/Lawson/archive/2012/05/20/2510781.html http://www.blogjava.net/wayn ...
- 执行update语句mysql5.6报错ERROR 1292 (22007): Truncated incorrect DOUBLE value: '糖糖的坤大叔'
执行修改语句update tbl_user_details set nickname=CONCAT("用户",yunva_id) where nickname = yunva_id ...
- centos常用网络管理命令
网卡配置命令:ifconfig (ip addr , ip link) ifconfig:显示所有活动状态的相关信息 ifconfig Interface:仅显示指定接口的相关信息 ifc ...
- 解决报错error the @annotation pointcut expression is only supported at Java 5
eclipse搭建环境后报错 error the @annotation pointcut expression is only supported at Java 5 错误意思大致是:注释切入点表达 ...
- transfer pdf to png
#! /bin/bash # # transfer pdf to png if [ $# != 1 ] ; then echo "USAGE: $0 PDF FILE ABSOLUTELY ...
- 【前端】js截取or分割字符串的常见方法
1.截取字符串 分割字符串方法 1.charAt(): 没有一种有别于字符串类型的字符数据类型,所以返回的字符是长度为 1 的字符串 例如:var str="Hello world!&quo ...
- sqlserver服务启动后停止,传递给数据库 'master' 中的日志扫描操作的日志扫描号无效
电脑异常重启,导致SqlServer服务启动后,自动停止,在[计算机管理]-[事件查看器]-[windows日志]中进行查看系统错误日志,在[应用程序]下发现可能的错误信息有以下两条: 1.错误:传递 ...
- HTML中的锚点设置和table格式
锚点设置: <a href="#1">锚点</a> <a name="1"></a> table表格格式: &l ...