JavaScript 代码执行一段可执行代码(executable code)时,会创建对应的执行环境,这个执行环境就叫做执行上下文(Execution Context);上下文又分为全局上下文和函数上下文

每个执行上下文会有三个重要的属性:

  1. 变量对象(Variable object,VO),基础数据类型就保存在这个变量中
  2. 作用域链(Scope chain)
  3. this

引用类型的值保存在堆里,我们通过引用地址来操作对象

  1. function task(){
  2. var a = 1;
  3. var b = {
  4. name:'zhufeng'
  5. }
  6. var c = [1,2,3];
  7. }
  1. let ExecutionContext{
  2. VO:{
  3. a:1,
  4. b:'X01'
  5. c:'XA1'
  6. }
  7. }

image.png

复制

基本数据类型

  • 基本数据类型复制的是值本身
    1. var a = 1;
    2. var b = a;
    3. b = 2;
    4. console.log(a);1
    ```javascript var ExecuteContext = { VO: { a: 1 } };

ExecuteContext.VO.b = ExecuteContext.VO.a; ExecuteContext.VO.b = 2; console.log(ExecuteContext.VO.a);

  1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/735369/1626881337088-a5d98733-f164-4df0-93a6-e5eeec2bfa59.png#clientId=uc49b4b5b-941d-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=163&id=u1b4a71bf&margin=%5Bobject%20Object%5D&name=image.png&originHeight=326&originWidth=884&originalType=binary&ratio=1&rotation=0&showTitle=false&size=32780&status=done&style=none&taskId=ud5d72111-9b23-4c81-a3dd-2dc23475e41&title=&width=442)
  2. <a name="q1z5n"></a>
  3. ## 引用类型
  4. - 引用数据类型复制的是引用地址指针
  5. ```javascript
  6. var m = { a: 1, b: 2 };
  7. var n = m;
  8. n.a = 10;
  9. console.log(m.a);// 10
  1. var ExecuteContext = {
  2. VO: { m: { a: 1, b: 2 } }
  3. };
  4. ExecuteContext.VO.b = ExecuteContext.VO.a;
  5. ExecuteContext.VO.a = 10;
  6. console.log(ExecuteContext.VO.a);

image.png

执行上下文周期

  • 分为编译阶段
    • 创建变量VO:
      • 处理参数,把参数放入vo
      • 扫描代码,找出function声明,从上往下依次执行,在编译阶段,会处理所有的函数声明,如果有重复的函数声明,后面的会覆盖前面的声明。
      • 扫描var关键字,var是不赋值的,只声明,值是undefined
      • 在编译阶段是不会扫描let的,也不会将let声明的变量放在VO上
    • 确定作用域链
      • 当前执行上下文的所有父级的vo对象[oneVO,globalVO]
    • 确定this指向
  • 执行阶段
    • 变量赋值
    • 函数赋值
    • 代码执行

执行栈(多执行上下文栈)

执行上下文的分类

  • js代码在执行的时候会进去一个执行上下文,可以理解为当前代码的运行环境
  • 在js运行环境主要分为全局执行上下文和函数执行上下文

    • 全局上下文只有一个,在客户端由浏览器创建,也就是我们熟知的window对象,我们可以通过this访问它
    • window对象还是var声明全局变量的载体,也就是我们可以通过window访问var创建的全局变量

      多个执行上下文

  • js在执行的时候创建多个执行上下文,js引擎会通过栈来管理这些上下文

  • 执行上下文栈,简称调用栈,执行栈用于存储代码在执行期间的上下文,具有LIFO(Last in First Output),后进先出
  • 栈底永远是全局上下文,栈顶为当前执行上下文
  • 当开启一个函数执行时会创建一个执行上下文存储在调用栈中,执行完毕后会自动出栈

    作用域链

    作用域链是当前执行环境与上层执行环境的一系列变量对象组成,它保证了当前执行环境对符合访问权限的变量和函数的有序访问
    1. function one() {
    2. var a = 1;
    3. debugger;
    4. function two() {
    5. var b = 1;
    6. debugger;
    7. function three() {
    8. var c = 1;
    9. debugger;
    10. }
    11. three();
    12. debugger;
    13. }
    14. two();
    15. debugger;
    16. }
    17. one();
    ```javascript var globalExecuteContext = { VO: { setTimeout: ‘setTimeout’ }, scopeChain:[globalExecuteContextVo] //作用域链 } var executeContextStack = [globalExecuteContext]; var oneExecuteContext = { VO: { a: 1 }, /// 作用域链创建时机是在上层执行上下来创建的时候就确定了 scopeChain:[oneExecuteContextVO,globalExecuteContextVo] //作用域链 } executeContextStack.push(oneExecuteContext); var twoExecuteContext = { VO: { b: 2 }, scopeChain:[twoExecuteContextVO,oneExecuteContextVO,globalExecuteContextVo] //作用域链 } executeContextStack.push(twoExecuteContext); var threeExecuteContext = { VO: { c: 3 }, scopeChain:[threeExecuteContextVO,twoExecuteContextVO,oneExecuteContextVO,globalExecuteContextVo] //作用域链 } executeContextStack.push(threeExecuteContext); console.log(executeContextStack);

executeContextStack.pop(); executeContextStack.pop(); executeContextStack.pop();

  1. <a name="cvwMa"></a>
  2. # 思考题
  3. 以下两段代码执行的结果一样,但是两段代码究竟有哪些不同呢?
  4. ```javascript
  5. var scope = "global scope";
  6. function checkscope(){
  7. var scope = "local scope";
  8. function f(){
  9. return scope;
  10. }
  11. return f();
  12. }
  13. checkscope();
  1. var scope = "global scope";
  2. function checkscope(){
  3. var scope = "local scope";
  4. function f(){
  5. return scope;
  6. }
  7. return f;
  8. }
  9. checkscope()();

答:执行上下文栈的变化不一样
第一段代码

  1. ECStack.push(<checkscope> functionContext);
  2. ECStack.push(<f> functionContext);
  3. ECStack.pop();
  4. ECStack.pop();

第二段代码

  1. ECStack.push(<checkscope> functionContext);
  2. ECStack.pop();
  3. ECStack.push(<f> functionContext);
  4. ECStack.pop();

JavaScript深入之变量对象