js上下文

概述

js程序执行的时候,必然涉及到执行上下文那么什么叫"执行上下文"呢?

本文介绍如下

  • 执行上下文的类型
  • 执行上下文特点
  • 执行栈
  • 执行上下文的生命周期

概念

举个例子,生活中,相同的话在不同的场合说可能会有不同的意思,而这个说话的场合就是我们说话的语境。

同样对应在编程中, 对程序语言进行"解读"的时候,也必须在特定的语境中,这个语境就是javascript中的执行上下文。

一句话概括:

执行上下文就是javascript代码被解析和执行时所在环境的抽象概念。

执行上下文的类型

在js中,执行上下文分为以下三种:

  • 全局执行上下文:只有一个,也就是浏览器对象(即window对象),this指向的就是这个全局对象。
  • 函数执行上下文 :有无数个,只有在函数被调用 时才会被创建,每次调用函数都会创建一个新的执行上下文。
  • Eval函数执行上下文:js的eval函数执行其内部的代码会创建属于自己的执行上下文, 很少用而且不建议使用。

执行上下文的特点

  1. 单线程,只在主线程上运行;
  2. 同步执行,从上向下按顺序执行;
  3. 全局上下文只有一个,也就是window对象;
  4. 函数执行上下文没有限制;
  5. 函数每调用一次就会产生一个新的执行上下文环境。

JS如何管理多个执行上下文

通过上面介绍,我们知道了js代码在运行时可能会产生无数个执行上下文,那么它是如何管理这些执行上下文的呢?

同时由于js是单线程的,所以不能同时干两件事,必须一个个去执行,那么这么多的执行上下文是按什么顺序执行的呢?

执行栈

接下来就对上面的问题做出解答,管理多个执行上下文靠的就是执行栈 ,也被叫做调用栈

特点:后进先出(LIFO)的结构。

作用:存储在代码执行期间的所有执行上下文。

(LIFO: last-in, first-out,类似于向乒乓球桶中放球,最先放入的球最后取出)

js在首次执行的时候,会创建一个全局执行上下文并推入栈中。

每当有函数被调用时,引擎都会为该函数创建一个新的函数执行上下文然后推入栈中。

当栈顶的函数执行完毕之后,该函数对应的执行上下文就会从执行栈中pop出,然后上下文控制权移到下一个执行上下文。

例子:

js 复制代码
var a = 1; // 1. 全局上下文环境
function bar (x) {
    console.log('bar')
    var b = 2;
    fn(x + b); // 3. fn上下文环境
}
function fn (c) {
    console.log(c);
}
bar(3); // 2. bar上下文环境

如下图:

执行上下文的生命周期

执行上下文的生命周期也非常容易理解, 分为三个阶段:

  1. 创建阶段
  2. 执行阶段
  3. 销毁阶段

创建阶段

创建阶段, 主要有是有这么几件事:

  1. 确定this 的值, 也就是绑定this (This Binding);
  2. 词法环境(LexicalEnvironment) 组件被创建;
  3. 变量环境(VariableEnvironment) 组件被创建.

伪代码

js 复制代码
ExecutionContext = {  
  ThisBinding = <this value>,     // 确定this 
  LexicalEnvironment = { ... },   // 词法环境
  VariableEnvironment = { ... },  // 变量环境
}

This Binding

通过上面的介绍我们知道实际开发主要用到两种执行上下文为全局函数, 那么绑定this在这两种上下文中也不同.

  • 全局执行上下文中, this指的就是全局对象, 浏览器环境指向window对象, nodejs中指向这个文件的module对象.
  • 函数执行上下文较为复杂, this的值取决于函数的调用方式. 具体有: 默认绑定、隐式绑定、显式绑定、new绑定、箭头函数.

词法环境

如上图, 词法环境 是由两个部分组成的:

  1. 环境记录: 存储变量和函数声明的实际位置;
  2. 对外部环境的引用: 用于访问其外部词法环境.

同样的, 词法环境也主要有两种类型:

  1. 全局环境 : 拥有一个全局对象(window对象)及其关联的所有属性和方法(比如数组的方法splice、concat等), 同时也包含了用户自定义的全局变量. 但是全局环境中没有外部环境的引用, 也就是外部环境引用为null.
  2. 函数环境 : 用户在函数中自定义的变量和函数存储在环境记录 中, 包含了arguments对象. 而对外部环境的引用可以是全局环境 , 也可以是另一个函数环境(比如一个函数中包含了另一个函数).

变量环境

变量环境其实也是一个词法环境, 因此它具有上面定义的词法环境的所有属性.

在 ES6 中,词法 环境和 变量 环境的区别在于前者用于存储函数声明和变量( let 和 const )绑定,而后者仅用于存储变量( var ) 绑定。

变量提升

在创建阶段,函数声明存储在环境中,而变量会被设置为 undefined(在 var 的情况下)或保持未初始化(在 let 和 const 的情况下)。所以这就是为什么可以在声明之前访问 var 定义的变量(尽管是 undefined ),但如果在声明之前访问 let 和 const 定义的变量就会提示引用错误的原因。这就是所谓的变量提升。

执行阶段

执行阶段主要做三件事情:

  1. 变量赋值
  2. 函数引用
  3. 执行其他的代码

注意

如果 Javascript 引擎在源代码中声明的实际位置找不到 let 变量的值,那么将为其分配 undefined 值。

销毁阶段

执行完毕出栈,等待回收被销毁

相关推荐
中草药z6 小时前
【Stream API】高效简化集合处理
java·前端·javascript·stream·parallelstream·并行流
不知名raver(学python版)7 小时前
npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR!
前端·npm·node.js
醉方休7 小时前
React中使用DDD(领域驱动设计)
前端·react.js·前端框架
excel7 小时前
📖 小说网站的预导航实战:link 预加载 + fetch + 前进后退全支持
前端
学习3人组7 小时前
React 样式隔离核心方法和最佳实践
前端·react.js·前端框架
世伟爱吗喽7 小时前
threejs入门学习日记
前端·javascript·three.js
朝阳5817 小时前
用 Rust + Actix-Web 打造“Hello, WebSocket!”——从握手到回声,只需 50 行代码
前端·websocket·rust
F2E_Zhangmo7 小时前
基于cornerstone3D的dicom影像浏览器 第五章 在Displayer四个角落显示信息
开发语言·前端·javascript
slim~8 小时前
javaweb基础第一天总结(HTML-CSS)
前端·css·html