执行上下文 (Execution Context): 函数执行前进行的准备工作 (也称执行上下文环境).
当执行代码进入一个环境时, 就会为该环境创建一个执行上下文, 它会做一些准备工作, 如变量提升, 确定作用域等.
一, 执行上下文的类型
JS 中有三种执行上下文类型:
全局执行上下文: 顶层上下文, 任何不在函数内部的代码都在其中. 上下文中的大哥大! 它干了两件事: 1. 创建一个全局的 Windows 对象 (浏览器环境下),2. 设置 this 的值等于这个全局对象.(一个程序中只会有一个全局执行上下文)
函数执行上下文: 函数被调用时, 会创建新的上下文, 可以有多个.
Eval 函数执行上下文: aval 函数专用, 用的不多, 没排面, 不讨论.
执行栈
在 JS 中, 通过栈的存取方式来管理执行上下文, 我们可称其为执行栈, 或函数调用栈 (Call Stack).
栈遵循 "先进先出, 后进后出" 的规则, 也就是 LIFO(Last In First Out) 规则, 可以用乒乓球盒子来类比理解.
特点:
1, 先进先出, 后进后出
2, 出口在顶部, 且仅有一个
当 JS 引擎第一次遇到你的脚本时, 它会创建一个全局的执行上下文并且压入当前执行栈. 而每当引擎遇到一个函数调用, 它会为该函数创建一个新的执行上下文并压入栈的顶部.
引擎会执行那些位于执行上下文顶栈顶的函数, 当函数执行结束时, 其执行上下文从栈中弹出, 控制流程达到当前栈中的下一个上下文.
我们可以通过示例代码来理解:
- function foo () {
- function bar () {
- return 'I am bar';
- }
- return bar();
- }
- foo();
二, 执行上下文的生命周期
执行上下文的生命周期有两个阶段:
创建阶段 (进入执行上下文, 如: 变量提升等)
执行阶段
创建阶段的操作:
1, 创建变量对象
函数环境会初始化创建 Arguments 对象 (并赋值), 并传递长度 length
匿名函数声明 (并赋值)
变量声明, 函数表达式声明 (未赋值, 即变量提升)
2, 确定 this 指向 (this 由调用者确定)
3, 确定作用域 (词法环境确定, 哪里声明, 哪里确定)
执行阶段的操作
1, 变量对象赋值
变量赋值
函数表达式赋值
2, 调用函数
3, 执行其他代码片段
值得注意的是, 执行上下文可存在多个, 因此在递归中可能因为没有终止条件而造成死循环, 堆栈溢出错误.
- // 递归调用自身
- function foo() {
- foo();
- }
- foo();
- // 报错: Uncaught RangeError: Maximum call stack size exceeded
文末总结:
1,JS 是单线程
2, 栈顶的执行上下文处于执行中时, 其他的需要排队
3, 处于底部的总是全局上下文, 关闭页面时出栈
4, 函数执行上下文可存在多个, 但应避免递归时堆栈溢出
5, 函数调用时就会创建新的上下文, 即使调用自身, 同样如此
参考文档:
- https://mp.weixin.qq.com/s/lAvyjfBZvX0E50QiW3aW7w
- https://juejin.im/post/5ba32171f265da0ab719a6d7(并未讨论其中的词法环境)
来源: http://www.jianshu.com/p/8e5ada9dd29d