前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JS学习系列 05 - 执行上下文

JS学习系列 05 - 执行上下文

作者头像
leocoder
发布2018-10-31 11:49:45
9530
发布2018-10-31 11:49:45
举报
文章被收录于专栏:前端进阶之路前端进阶之路

在我们前面理解了作用域之后,“作用域链”这个概念就产生了。那么作用域链是什么意思,它又是怎么形成的,跟哪些概念有关系,这就是我接下来几章想和大家探讨的内容:执行上下文、变量对象和作用域链。根据顺序我们也可以看出来,想要理解作用域链,执行上下文是我们碰到的第一个坎。

这一章我们就来讨论一下到底什么是执行上下文

1. 定义

当 JS 引擎开始执行预编译生成的代码时,就会进入到一个执行上下文(Executable Code - 简称 EC)。

在 ECMA 标准规范里并没有从技术角度去定义 EC 的具体类型和结构,这个是在实现 ECMAScript 引擎时需要考虑的问题。

但是在逻辑上,我们可以将活动的执行上下文看成一个栈结构。栈底部永远是全局上下文(global context),而顶部就是当前活动的执行上下文。执行到当前代码时,上下文入栈,执行完毕后,上下文出栈。

2. 可执行代码有几种

前面说到当引擎执行到可执行代码的时候,就会将当前上下文压入上下文栈中。那么可执行的代码又分为几种?

在这里,我们先假设定义执行上下文栈是一个数组:

代码语言:javascript
复制
EC = [];
复制代码

第一种可执行代码 -- 全局代码: 全局类型代码是在加载外部的 js 文件或者本地 标签中的代码。 注意,在全局代码中,并不包含定义在全局环境 function 内的代码

程序启动后进入初始化全局环境:

代码语言:javascript
复制
EC = [
    globalContext
];
复制代码

第二种可执行代码 -- 函数代码: 当定义的函数被执行时,就进入了函数代码,当前函数上下文被压入 EC 栈中。 注意,在函数代码中,也不包含定义在该函数内部环境 function 内的代码。

例如:

代码语言:javascript
复制
var a = 10;

function foo () {
  var b = 20;
  
  foo();
}

foo();
复制代码

这个例子中的 EC 是什么样子的呢?

代码语言:javascript
复制
// 初始化
EC = [
  globalContext
];

// 第一次调用 foo 函数
EC = [
  <foo> functionContext,
  globalContext
];

// 在 foo 内递归调用自己
EC = [
  <foo> functionContext - recursively,
  <foo> functionContext,
  globalContext
];

// 继续递归调用自己
EC = [
  ......
  <foo> functionContext - recursively2,
  <foo> functionContext - recursively,
  <foo> functionContext,
  globalContext
];

// 递归会不断调用下去,因为没有结束条件,所以这是一个死循环
// 所以,EC 只会不断增加新的上下文,但是却不会退出
复制代码

只有每次 return 的时候,才会退出当前执行上下文,相应上下文会从栈中弹出,栈指针会自动移动位置。

注意,当函数没有明确指明 return 什么的时候,默认 return undefined

如果有抛出的异常没有被截获的话,也有可能从一个或多个执行上下文中退出。当所有代码执行完以后,EC 中只会包含全局上下文(global context),当程序退出以后,全局上下文也会退出。

第三种可执行代码 -- eval 代码: eval 函数在调用的时候会产生上下文。 例如:

代码语言:javascript
复制
eval('var a = 10');

(function foo () {
  eval('var b = 20');
}());

alert(a);    // 10
alert(b);    // ReferenceError,b is not defined 
复制代码

这个例子中 EC 的变化如下:

代码语言:javascript
复制
// 初始化
EC = [
  globalContext
];

// eval('var a = 10');
EC = [
  evalContext,
  globalContext
];

// eval 执行完毕
EC = [
  globalContext
];

// 立即执行函数 foo
EC = [
  <foo> functionContext,
  globalContext
];

// eval('var b = 20');
EC = [
  evalContext,
  <foo> functionContext,
  globalContext
];

// eval 执行完毕
EC = [
  <foo> functionContext,
  globalContext
];

// foo 执行完毕
EC = [
  globalContext
];
复制代码

这就是一个典型的逻辑调用上下文栈

在 setTimeout 和 setInterval 函数中的第一个参数也可以传入代码字符串,但是这个一般不会这么去用,所以这里也就不讨论了。

3. 结论

执行上下文环境是我们了解变量对象作用域链的基础,大家一定要好好理解(其实也并不难),下一节我们来讨论变量对象,相信会让大家有一定的收获。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年04月11日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 定义
  • 2. 可执行代码有几种
  • 3. 结论
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档