ECMAScript 作用域链

前言

ECMAScript标准是深入学习JavaScript原理最好的资料,没有其二。

通过增加对ECMAScript语言的理解,理解javascript现象后面的逻辑,提升个人编码能力。

欢迎关注和订阅专栏 **[重学前端-ECMAScript协议上篇]

示例

基于上个示例,做一些调整,下面的代码又是如何输出的呢,以及其背后执行上下文,环境记录又是怎样运作的呢。

xml 复制代码
javascript
复制代码
<script>
  "use strict"
  var varA = 'varA';
  const constA = 'constA';

  {
    var varA = 'block_varA';
    let constA = 'block_constA';
  }

  function log(){
    const constA = 'log_constA';
    console.log(varA, constA);
  }
  log();
</script>

这段代码就得分两段来分析了

  • log 方法调用前
  • log 方法的执行时

全局代码

代码执行的基本流程

和之前的基本逻辑完全一样

ParseScript

返回的脚本记录的[[ECMAScriptCode]] 大致的内容如下:

json 复制代码
javascript
复制代码
{
    "type": "Script",
    "location": {
        "startIndex": 2,
        "endIndex": 229,
        "start": {
            "line": 1,
            "column": 3
        },
        "end": {
            "line": 14,
            "column": 9
        }
    },
    "strict": false,
    "ScriptBody": {
        "type": "ScriptBody",
        "location": {
            "startIndex": 2,
            "endIndex": 229,
            "start": {
                "line": 1,
                "column": 3
            },
            "end": {
                "line": 14,
                "column": 9
            }
        },
        "strict": true,
        "StatementList": [
            {
                "type": "ExpressionStatement",
                "location": {
                    "startIndex": 2,
                    "endIndex": 14,
                    "start": {
                        "line": 1,
                        "column": 3
                    },
                    "end": {
                        "line": 1,
                        "column": 3
                    }
                },
                "strict": true,
                "Expression": {
                    "type": "StringLiteral",
                    "location": {
                        "startIndex": 2,
                        "endIndex": 14,
                        "start": {
                            "line": 1,
                            "column": 3
                        },
                        "end": {
                            "line": 1,
                            "column": 3
                        }
                    },
                    "strict": true,
                    "value": "use strict"
                }
            },
            {
                "type": "VariableStatement",
                "location": {
                    "startIndex": 17,
                    "endIndex": 35,
                    "start": {
                        "line": 2,
                        "column": 3
                    },
                    "end": {
                        "line": 2,
                        "column": 20
                    }
                },
                "strict": true,
                "VariableDeclarationList": [
                    {
                        "type": "VariableDeclaration",
                        "location": {
                            "startIndex": 21,
                            "endIndex": 34,
                            "start": {
                                "line": 2,
                                "column": 7
                            },
                            "end": {
                                "line": 2,
                                "column": 14
                            }
                        },
                        "strict": true,
                        "BindingIdentifier": {
                            "type": "BindingIdentifier",
                            "location": {
                                "startIndex": 21,
                                "endIndex": 25,
                                "start": {
                                    "line": 2,
                                    "column": 7
                                },
                                "end": {
                                    "line": 2,
                                    "column": 7
                                }
                            },
                            "strict": true,
                            "name": "varA"
                        },
                        "Initializer": {
                            "type": "StringLiteral",
                            "location": {
                                "startIndex": 28,
                                "endIndex": 34,
                                "start": {
                                    "line": 2,
                                    "column": 14
                                },
                                "end": {
                                    "line": 2,
                                    "column": 14
                                }
                            },
                            "strict": true,
                            "value": "varA"
                        }
                    }
                ]
            },
            {
                "type": "LexicalDeclaration",
                "location": {
                    "startIndex": 38,
                    "endIndex": 62,
                    "start": {
                        "line": 3,
                        "column": 3
                    },
                    "end": {
                        "line": 3,
                        "column": 26
                    }
                },
                "strict": true,
                "LetOrConst": "const",
                "BindingList": [
                    {
                        "type": "LexicalBinding",
                        "location": {
                            "startIndex": 44,
                            "endIndex": 61,
                            "start": {
                                "line": 3,
                                "column": 9
                            },
                            "end": {
                                "line": 3,
                                "column": 18
                            }
                        },
                        "strict": true,
                        "BindingIdentifier": {
                            "type": "BindingIdentifier",
                            "location": {
                                "startIndex": 44,
                                "endIndex": 50,
                                "start": {
                                    "line": 3,
                                    "column": 9
                                },
                                "end": {
                                    "line": 3,
                                    "column": 9
                                }
                            },
                            "strict": true,
                            "name": "constA"
                        },
                        "Initializer": {
                            "type": "StringLiteral",
                            "location": {
                                "startIndex": 53,
                                "endIndex": 61,
                                "start": {
                                    "line": 3,
                                    "column": 18
                                },
                                "end": {
                                    "line": 3,
                                    "column": 18
                                }
                            },
                            "strict": true,
                            "value": "constA"
                        }
                    }
                ]
            },
            {
                "type": "Block",
                "location": {
                    "startIndex": 66,
                    "endIndex": 133,
                    "start": {
                        "line": 5,
                        "column": 3
                    },
                    "end": {
                        "line": 8,
                        "column": 3
                    }
                },
                "strict": true,
                "StatementList": [
                    {
                        "type": "VariableStatement",
                        "location": {
                            "startIndex": 72,
                            "endIndex": 96,
                            "start": {
                                "line": 6,
                                "column": 5
                            },
                            "end": {
                                "line": 6,
                                "column": 28
                            }
                        },
                        "strict": true,
                        "VariableDeclarationList": [
                            {
                                "type": "VariableDeclaration",
                                "location": {
                                    "startIndex": 76,
                                    "endIndex": 95,
                                    "start": {
                                        "line": 6,
                                        "column": 9
                                    },
                                    "end": {
                                        "line": 6,
                                        "column": 16
                                    }
                                },
                                "strict": true,
                                "BindingIdentifier": {
                                    "type": "BindingIdentifier",
                                    "location": {
                                        "startIndex": 76,
                                        "endIndex": 80,
                                        "start": {
                                            "line": 6,
                                            "column": 9
                                        },
                                        "end": {
                                            "line": 6,
                                            "column": 9
                                        }
                                    },
                                    "strict": true,
                                    "name": "varA"
                                },
                                "Initializer": {
                                    "type": "StringLiteral",
                                    "location": {
                                        "startIndex": 83,
                                        "endIndex": 95,
                                        "start": {
                                            "line": 6,
                                            "column": 16
                                        },
                                        "end": {
                                            "line": 6,
                                            "column": 16
                                        }
                                    },
                                    "strict": true,
                                    "value": "block_varA"
                                }
                            }
                        ]
                    },
                    {
                        "type": "LexicalDeclaration",
                        "location": {
                            "startIndex": 101,
                            "endIndex": 129,
                            "start": {
                                "line": 7,
                                "column": 5
                            },
                            "end": {
                                "line": 7,
                                "column": 32
                            }
                        },
                        "strict": true,
                        "LetOrConst": "let",
                        "BindingList": [
                            {
                                "type": "LexicalBinding",
                                "location": {
                                    "startIndex": 105,
                                    "endIndex": 128,
                                    "start": {
                                        "line": 7,
                                        "column": 9
                                    },
                                    "end": {
                                        "line": 7,
                                        "column": 18
                                    }
                                },
                                "strict": true,
                                "BindingIdentifier": {
                                    "type": "BindingIdentifier",
                                    "location": {
                                        "startIndex": 105,
                                        "endIndex": 111,
                                        "start": {
                                            "line": 7,
                                            "column": 9
                                        },
                                        "end": {
                                            "line": 7,
                                            "column": 9
                                        }
                                    },
                                    "strict": true,
                                    "name": "constA"
                                },
                                "Initializer": {
                                    "type": "StringLiteral",
                                    "location": {
                                        "startIndex": 114,
                                        "endIndex": 128,
                                        "start": {
                                            "line": 7,
                                            "column": 18
                                        },
                                        "end": {
                                            "line": 7,
                                            "column": 18
                                        }
                                    },
                                    "strict": true,
                                    "value": "block_constA"
                                }
                            }
                        ]
                    }
                ]
            },
            {
                "type": "FunctionDeclaration",
                "location": {
                    "startIndex": 137,
                    "endIndex": 220,
                    "start": {
                        "line": 10,
                        "column": 3
                    },
                    "end": {
                        "line": 13,
                        "column": 3
                    }
                },
                "strict": true,
                "BindingIdentifier": {
                    "type": "BindingIdentifier",
                    "location": {
                        "startIndex": 146,
                        "endIndex": 149,
                        "start": {
                            "line": 10,
                            "column": 12
                        },
                        "end": {
                            "line": 10,
                            "column": 12
                        }
                    },
                    "strict": true,
                    "name": "log"
                },
                "FormalParameters": [],
                "FunctionBody": {
                    "type": "FunctionBody",
                    "location": {
                        "startIndex": 151,
                        "endIndex": 220,
                        "start": {
                            "line": 10,
                            "column": 17
                        },
                        "end": {
                            "line": 13,
                            "column": 3
                        }
                    },
                    "strict": true,
                    "directives": [],
                    "FunctionStatementList": [
                        {
                            "type": "LexicalDeclaration",
                            "location": {
                                "startIndex": 157,
                                "endIndex": 185,
                                "start": {
                                    "line": 11,
                                    "column": 5
                                },
                                "end": {
                                    "line": 11,
                                    "column": 32
                                }
                            },
                            "strict": true,
                            "LetOrConst": "const",
                            "BindingList": [
                                {
                                    "type": "LexicalBinding",
                                    "location": {
                                        "startIndex": 163,
                                        "endIndex": 184,
                                        "start": {
                                            "line": 11,
                                            "column": 11
                                        },
                                        "end": {
                                            "line": 11,
                                            "column": 20
                                        }
                                    },
                                    "strict": true,
                                    "BindingIdentifier": {
                                        "type": "BindingIdentifier",
                                        "location": {
                                            "startIndex": 163,
                                            "endIndex": 169,
                                            "start": {
                                                "line": 11,
                                                "column": 11
                                            },
                                            "end": {
                                                "line": 11,
                                                "column": 11
                                            }
                                        },
                                        "strict": true,
                                        "name": "constA"
                                    },
                                    "Initializer": {
                                        "type": "StringLiteral",
                                        "location": {
                                            "startIndex": 172,
                                            "endIndex": 184,
                                            "start": {
                                                "line": 11,
                                                "column": 20
                                            },
                                            "end": {
                                                "line": 11,
                                                "column": 20
                                            }
                                        },
                                        "strict": true,
                                        "value": "log_constA"
                                    }
                                }
                            ]
                        },
                        {
                            "type": "ExpressionStatement",
                            "location": {
                                "startIndex": 190,
                                "endIndex": 216,
                                "start": {
                                    "line": 12,
                                    "column": 5
                                },
                                "end": {
                                    "line": 12,
                                    "column": 30
                                }
                            },
                            "strict": true,
                            "Expression": {
                                "type": "CallExpression",
                                "location": {
                                    "startIndex": 190,
                                    "endIndex": 215,
                                    "start": {
                                        "line": 12,
                                        "column": 5
                                    },
                                    "end": {
                                        "line": 12,
                                        "column": 29
                                    }
                                },
                                "strict": true,
                                "CallExpression": {
                                    "type": "MemberExpression",
                                    "location": {
                                        "startIndex": 190,
                                        "endIndex": 201,
                                        "start": {
                                            "line": 12,
                                            "column": 5
                                        },
                                        "end": {
                                            "line": 12,
                                            "column": 13
                                        }
                                    },
                                    "strict": true,
                                    "MemberExpression": {
                                        "type": "IdentifierReference",
                                        "location": {
                                            "startIndex": 190,
                                            "endIndex": 197,
                                            "start": {
                                                "line": 12,
                                                "column": 5
                                            },
                                            "end": {
                                                "line": 12,
                                                "column": 5
                                            }
                                        },
                                        "strict": true,
                                        "escaped": false,
                                        "name": "console"
                                    },
                                    "IdentifierName": {
                                        "type": "IdentifierName",
                                        "location": {
                                            "startIndex": 198,
                                            "endIndex": 201,
                                            "start": {
                                                "line": 12,
                                                "column": 13
                                            },
                                            "end": {
                                                "line": 12,
                                                "column": 13
                                            }
                                        },
                                        "strict": true,
                                        "name": "log"
                                    },
                                    "PrivateIdentifier": null,
                                    "Expression": null
                                },
                                "Arguments": [
                                    {
                                        "type": "IdentifierReference",
                                        "location": {
                                            "startIndex": 202,
                                            "endIndex": 206,
                                            "start": {
                                                "line": 12,
                                                "column": 17
                                            },
                                            "end": {
                                                "line": 12,
                                                "column": 17
                                            }
                                        },
                                        "strict": true,
                                        "escaped": false,
                                        "name": "varA"
                                    },
                                    {
                                        "type": "IdentifierReference",
                                        "location": {
                                            "startIndex": 208,
                                            "endIndex": 214,
                                            "start": {
                                                "line": 12,
                                                "column": 23
                                            },
                                            "end": {
                                                "line": 12,
                                                "column": 23
                                            }
                                        },
                                        "strict": true,
                                        "escaped": false,
                                        "name": "constA"
                                    }
                                ]
                            }
                        }
                    ]
                }
            },
            {
                "type": "ExpressionStatement",
                "location": {
                    "startIndex": 223,
                    "endIndex": 229,
                    "start": {
                        "line": 14,
                        "column": 3
                    },
                    "end": {
                        "line": 14,
                        "column": 8
                    }
                },
                "strict": true,
                "Expression": {
                    "type": "CallExpression",
                    "location": {
                        "startIndex": 223,
                        "endIndex": 228,
                        "start": {
                            "line": 14,
                            "column": 3
                        },
                        "end": {
                            "line": 14,
                            "column": 7
                        }
                    },
                    "strict": true,
                    "CallExpression": {
                        "type": "IdentifierReference",
                        "location": {
                            "startIndex": 223,
                            "endIndex": 226,
                            "start": {
                                "line": 14,
                                "column": 3
                            },
                            "end": {
                                "line": 14,
                                "column": 3
                            }
                        },
                        "strict": true,
                        "escaped": false,
                        "name": "log"
                    },
                    "Arguments": []
                }
            }
        ]
    }
}

去掉与本示例不太重要的节点,绘制成 tree,外加上对应的代码,如下:

整段代码也就六个语句

语句 说明 对应代码
ExpressionStatement 表达式语句 "use strict"
VariableStatement 变量申明语句 var varA = 'varA' ;
LexicalDeclaration 词法申明 const constA = 'constA' ;
Block {var varA = 'block_varA';let constA = 'block_constA';}
FunctionDeclaration 函数申明 function log(){const constA = 'log_constA';console.log(varA, constA);}
ExpressionStatement 表达式语句 log();

解析变量申明和词法申明

VarDeclaredNames

唯一需要注意的是, 函数申明 在 脚本或者函数的顶层代码中的表现和变量申明是一致的,所以值为:

`'varA'`, `'varA'`, `'log'`

LexicallyDeclaredNames

Script和函数顶层词法申明标识符查找不会遍历 Block类型的子节点, 所以返回值:

`constA`

申明初始化

全局环境记录又是由 对象环境记录 和 申明环境记录组成的, 词法申明和变量申明对应的绑定关系如下。

VarDeclaredNames LexicallyDeclaredNames
创建方法 env.CreateGlobalVarBinding(var)env.CreateGlobalFunctionBinding (function) env.CreateMutableBinding (let/class)env.CreateImmutableBinding(const)
对象环境记录
申明环境记录

从上面的解析变量申明和词法申明,得知结果:

  • VarDeclaredNames : ['varA', 'varA''log']
  • LexicallyDeclaredNames:['constA']

最后的关系图如下:

和之前的没什么不同,只是

  • 全局对象环境记录 和 全局对象上 多了一个 log 属性,并且有值
  • 全局环境记录 的VarNames 中多了个 log

function object

这里必须得提一下 log 的值 funciton object, 不然后面的逻辑没法继续,需要了解几点

  • 什么时候被创建的?
  • 其有一个 [[ECMAScriptCode]] 内部属性,是其内部代码的解析节点, 什么时候被赋值的?
  • 其有一个 [[Environment]] 内部属性,函数被定义时所处的环境记录。

什么时候被创建的呢?

在全局顶层代码 申明实例化的时候 GlobalDeclarationInstantiation ( script, env ) 16步骤

function object 的 [ECMAScriptCode]]属性 什么时候被赋值的? 函数对象被创建的时就赋值了。(如果图片模糊,放大观看)

  • 函数对象的[[Environment]] 内部属性,指向当时关联的环境记录 这一点很重要, 很重要,真重要。

全局顶层代码执行

代码是怎么执行的呢? 协议一句 Evaluation of script, 详情参见单独的章节。

最终你可以简单理解为 就是 挨个 执行 Script 节点下的 StatementList,前面已经罗列了,本示例一共六个语句。

伪代码如下:

scss 复制代码
javascript
复制代码
// ScriptBody : StatementList
function Evaluate_ScriptBody(ScriptBody) {
  return Evaluate_StatementList(ScriptBody.StatementList);
}

就直接跳到 log(); 这个语句前,最后执行上下文,环境记录和标识符绑定的关系图如下:

主要就是标识符都有值了。

log 函数执行

最后一个语句是函数调用,其解析节点信息如下,

json 复制代码
javascriptjavascript
复制代码
{
    "type": "ExpressionStatement",
    "location": {
        "startIndex": 223,
        "endIndex": 229,
        "start": {
            "line": 14,
            "column": 3
        },
        "end": {
            "line": 14,
            "column": 8
        }
    },
    "strict": true,
    "Expression": {
        "type": "CallExpression",
        "location": {
            "startIndex": 223,
            "endIndex": 228,
            "start": {
                "line": 14,
                "column": 3
            },
            "end": {
                "line": 14,
                "column": 7
            }
        },
        "strict": true,
        "CallExpression": {
            "type": "IdentifierReference",
            "location": {
                "startIndex": 223,
                "endIndex": 226,
                "start": {
                    "line": 14,
                    "column": 3
                },
                "end": {
                    "line": 14,
                    "column": 3
                }
            },
            "strict": true,
            "escaped": false,
            "name": "log"
        },
        "Arguments": []
    }
}

执行逻辑就是拿到 ExpressionStatement 的 Expression 进行调用,Expression节点类型为 CallExpression,调用的基本逻辑用伪代码编写如下

csharp 复制代码
javascript
复制代码
function* Evaluate_CallExpression(CallExpression) {
  // type 为 CallExpression 节点
  const expr = CallExpression; 
  //  type 为 "IdentifierReference" 节点,后面会用 其 name的值即log,查找函数对象
  const memberExpr = expr.CallExpression;
  //  参数列表
  const args = expr.Arguments;
  //  通过标志符名,查找函数对象 对应的引用记录
  let ref = yield* ResolveBinding(memberExpr.name, undefined, memberExpr.strict);

  // 从引用记录中 取值,获取函数对象 即 log 函数
  let func = GetValue(ref);

  // 是不是 处于尾部位置
  const tailCall = IsInTailPosition();
  // 执行 函数调用
  return yield* EvaluateCall(func, ref, args, tailCall);
}

本章节主要是说作用域和作用域链,函数的申明实例化和调用过程,不是本节的主要目的。 但还是得说明几点

  • log标志符绑定: 全局顶层代码 申明实例化的时候,而且其对应的值 函数对象也被初始化好了,所以这里可以通过上下文通过标志符查找到,然后进行调用。
  • 函数调用是会创建新的执行上下文的
  • 函数调用默认会新建一个函数环境记录,但是会根据函数 是不是有参数表达式,是不是严格模式会不创建或者创建不同个数的环境记录。(详情参见 函数调用的章节)

PrepareForOrdinaryCall 新建 函数执行上下文 和 函数环境记录

函数执行上下文和函数环境记录都是在 函数调用准备工作的 PrepareForOrdinaryCall创建的。

请记住第六步的这个操作 6. Set env.[[OuterEnv]] to F.[[Environment]]. 这是形成链的操作,也是实现闭包的核心。

PrepareForOrdinaryCall执行完毕之后 ,因为创建了新的执行上下文和环境记录,执行上下文,环境记录的关系图就变成下面的样子了:

log 函数申明实例化

可能有同志注意到了,函数首行有 "use strict",这是为了简单 函数申明实例化过程,让其不去创建新的 环境记录。

javascript 复制代码
javascript
复制代码
function log(){
  "use strict"
  const constA = 'log_constA';
  console.log(varA, constA);
}

简单分析一下,得出

标志符
VarDeclaredNames []
LexicallyDeclaredNames [constA]

函数执行是一个特别的标志符,就是 arguments,

  • 不属于 VarDeclaredNames 也不属于 LexicallyDeclaredNames 的结果中
  • 可能是可变的,也可能是不可变的

函数申明实例化的具体逻辑 是 10.2.11 FunctionDeclarationInstantiation ( func, argumentsList ) 定义的,流程大致有近30步骤,非常复杂。

但是,本示属于 严格模式 + 无函数参数表达式 的搭配,不会再新建任何环境记录, arguments 和 constA 的绑定关系都存在于 函数环境记录上。

申明实例化完毕后, 执行上下文和环境记录关系图如下:

主要就是函数环境记录上多了两个绑定关系。

log 函数代码执行 和 标志符绑定查找

代码的执行本质就是 log 标志符对应的 function object ,执行其 [[ECMAScriptCode]] 属性解析节点 的过程,其节点信息如下:

json 复制代码
javascript
复制代码
{
    "type": "FunctionBody",
    "location": {
        "startIndex": 151,
        "endIndex": 220,
        "start": {
            "line": 10,
            "column": 17
        },
        "end": {
            "line": 13,
            "column": 3
        }
    },
    "strict": true,
    "directives": [],
    "FunctionStatementList": [
        {
            "type": "LexicalDeclaration",
            "location": {
                "startIndex": 157,
                "endIndex": 185,
                "start": {
                    "line": 11,
                    "column": 5
                },
                "end": {
                    "line": 11,
                    "column": 32
                }
            },
            "strict": true,
            "LetOrConst": "const",
            "BindingList": [
                {
                    "type": "LexicalBinding",
                    "location": {
                        "startIndex": 163,
                        "endIndex": 184,
                        "start": {
                            "line": 11,
                            "column": 11
                        },
                        "end": {
                            "line": 11,
                            "column": 20
                        }
                    },
                    "strict": true,
                    "BindingIdentifier": {
                        "type": "BindingIdentifier",
                        "location": {
                            "startIndex": 163,
                            "endIndex": 169,
                            "start": {
                                "line": 11,
                                "column": 11
                            },
                            "end": {
                                "line": 11,
                                "column": 11
                            }
                        },
                        "strict": true,
                        "name": "constA"
                    },
                    "Initializer": {
                        "type": "StringLiteral",
                        "location": {
                            "startIndex": 172,
                            "endIndex": 184,
                            "start": {
                                "line": 11,
                                "column": 20
                            },
                            "end": {
                                "line": 11,
                                "column": 20
                            }
                        },
                        "strict": true,
                        "value": "log_constA"
                    }
                }
            ]
        },
        {
            "type": "ExpressionStatement",
            "location": {
                "startIndex": 190,
                "endIndex": 216,
                "start": {
                    "line": 12,
                    "column": 5
                },
                "end": {
                    "line": 12,
                    "column": 30
                }
            },
            "strict": true,
            "Expression": {
                "type": "CallExpression",
                "location": {
                    "startIndex": 190,
                    "endIndex": 215,
                    "start": {
                        "line": 12,
                        "column": 5
                    },
                    "end": {
                        "line": 12,
                        "column": 29
                    }
                },
                "strict": true,
                "CallExpression": {
                    "type": "MemberExpression",
                    "location": {
                        "startIndex": 190,
                        "endIndex": 201,
                        "start": {
                            "line": 12,
                            "column": 5
                        },
                        "end": {
                            "line": 12,
                            "column": 13
                        }
                    },
                    "strict": true,
                    "MemberExpression": {
                        "type": "IdentifierReference",
                        "location": {
                            "startIndex": 190,
                            "endIndex": 197,
                            "start": {
                                "line": 12,
                                "column": 5
                            },
                            "end": {
                                "line": 12,
                                "column": 5
                            }
                        },
                        "strict": true,
                        "escaped": false,
                        "name": "console"
                    },
                    "IdentifierName": {
                        "type": "IdentifierName",
                        "location": {
                            "startIndex": 198,
                            "endIndex": 201,
                            "start": {
                                "line": 12,
                                "column": 13
                            },
                            "end": {
                                "line": 12,
                                "column": 13
                            }
                        },
                        "strict": true,
                        "name": "log"
                    },
                    "PrivateIdentifier": null,
                    "Expression": null
                },
                "Arguments": [
                    {
                        "type": "IdentifierReference",
                        "location": {
                            "startIndex": 202,
                            "endIndex": 206,
                            "start": {
                                "line": 12,
                                "column": 17
                            },
                            "end": {
                                "line": 12,
                                "column": 17
                            }
                        },
                        "strict": true,
                        "escaped": false,
                        "name": "varA"
                    },
                    {
                        "type": "IdentifierReference",
                        "location": {
                            "startIndex": 208,
                            "endIndex": 214,
                            "start": {
                                "line": 12,
                                "column": 23
                            },
                            "end": {
                                "line": 12,
                                "column": 23
                            }
                        },
                        "strict": true,
                        "escaped": false,
                        "name": "constA"
                    }
                ]
            }
        }
    ]
}

本质上也就是执行 两个 FunctionStatementList 的 表达式语句

表达式 代码
LexicalDeclaration const constA = 'log_constA';
ExpressionStatement console.log(varA, constA);

const constA = 'log_constA'; 执行完毕之后,函数环境记录 constA 就有值 constA

console.log(varA, constA);本质也是函数调用,不过 console.log是内置函数,和用户自定义的函数是有一定的区别,不过这不是本章节的重点,本章节重点是作用域链。

先看看 varA的查找路径,就是红色标出来的路径。

函数环境记录 通过 [[outetEnv]] 链接外部的全局环境记录,形成了链条一般的关系,这就是作用域链的直观解释。 ****

再看看 constA的查找路径,就是红色标出的路径。

从下面的关系也可以知道

  • log 函数内的 constA ,在外面 Script执行上下文 里面的脚本 是根本访问不到的
  • log 函数 却能 访问全局环境记录中的 varA

这就是作用域的直观表示, 只能在一定的链中可以被访问到。

Block

到目前为止,都没细说过 块(Block) 里面的代码。 ES6 之后, 块里面的词法申明,是只能在块中被访问的。

比如如下代码:

xml 复制代码
javascript
复制代码
<script>
  "use strict"
  var varA = 'varA';
  {
    var varA = 'block_varA';
    function constA(){};
    console.log(varA, constA);    // block_varA   ƒ constA() { }
  }
  console.log(varA, constA);      // Uncaught ReferenceError: constA is not defined

</script>

至于全局顶层代码的 console.log(varA, constA)为什么会报错,理解 Block 的 运作逻辑 是非要必要的,对应着协议的 14.2 Block 的子章节 14.2.2 Runtime Semantics: Evaluation

Block 会新建申明环境记录

Block 的评估过程,会新建申明环境记录 , 作用嘛, 用于保存 词法申明 对应的绑定关系,就是 const/let/class等的申明。 至于块内变量申明 (var/function等),其是不会保存的。

特别注意:

  • 第4步骤,执行上下文把 LexicalEnvironment指向了新的申明环境记录, 第6步骤进行了复原
  • 从头到尾,执行上下文 没有更改 VariableEnvironment的指向。

Block 的 申明实例化

申明实例化的逻辑在协议的 14.2.3 BlockDeclarationInstantiation ( code, env )

实际上逻辑非常简单,就是先拿到所有的词法申明节点,然后创建绑定关系。这个 LexicallyScopedDeclarations

LexicallyDeclaredNames 类似, 不过返回的是 解析节点(Parse Node)而不是 标志符名。

之前有多次提到, LexicallyDeclaredNames 在Script 和 函数顶层代码是不包含 函数申明的,函数申明会作为变量申明处理。

重点来了, Block 块里面的代码,是不是顶层代码? 答案: 不是

因此,这里 LexicallyDeclaredNamesLexicallyScopedDeclaration 是包含 函数申明的。

Block 进入前后 作用域链的变化

通过列出三种状态的关系图,一起来看作用域的变化

  • 进入Block 之前
  • Block 内执行 console.log(varA, constA)
  • 退出Block后,顶层代码执行 console.log(varA, constA)

进入Block之前

Block 内 console.log(varA, constA) 执行前

新建了申明环境记录,执行上文的 LexicalEnvironment指向了 新的申明环境。

  • varA的查找:

    • 查找 LexicalEnvironment链路 未找到,
    • 再查找 VariableEnvironment链路,找到。
  • constA的查找:

    • 查找 LexicalEnvironment链路 ,在 Block块创建的申明环境记录中找到

退出Block后,顶层代码执行 console.log(varA, constA)

重新更改了执行上下的 LexicalEnvironment指向,恢复指向全局环境记录。

  • varA的查找:

    • 查找 LexicalEnvironment链路 未找到,
    • 再查找 VariableEnvironment链路,找到。
  • constA的查找:

    • 查找 LexicalEnvironment链路 未找到
    • 再查找 VariableEnvironment链路,未找到。

小结

  • Block 块 会新建环境记录,不会新建执行上下文

  • 通过上下文和环境记录的更改,实现 "动态" 的作用域链

    • 更改执行上下文 执行上下文 LexicalEnvironmentVariableEnvironment 的指向
    • 以及修改环境记录的 [[OuterEnv]]的指向
  • Block 块 内的函数申明,属于词法申明 (起码严格模式是)

思考

对本文开头的代码稍微做点修改,删除顶部的 "use Strict", 当 log函数执行到 console.log(varA, constA);

执行上下文,环境记录,标志符绑定又是怎样的呢?

ini 复制代码
html
复制代码
<script>
  var varA = 'varA';
  const constA = 'constA';

  {
    var varA = 'block_varA';
    let constA = 'block_constA';
  }

  function log(){
    const constA = 'log_constA';
    console.log(varA, constA);
  }
  log();
</script>

上一章

下一章

相关推荐
layman052810 分钟前
node.js 实战——(fs模块 知识点学习)
javascript·node.js
毕小宝10 分钟前
编写一个网页版的音频播放器,AI 加持,So easy!
前端·javascript
万水千山走遍TML10 分钟前
JavaScript性能优化
开发语言·前端·javascript·性能优化·js·js性能
Aphasia31111 分钟前
react必备JS知识点(一)——判断this指向👆🏻
前端·javascript·react.js
会飞的鱼先生27 分钟前
vue3中slot(插槽)的详细使用
前端·javascript·vue.js
小小小小宇41 分钟前
一文搞定CSS Grid布局
前端
0xHashlet1 小时前
Dapp实战案例002:从零部署链上计数器合约并实现前端交互
前端
知心宝贝1 小时前
🔍 从简单到复杂:JavaScript 事件处理的全方位解读
前端·javascript·面试
安余生大大1 小时前
关于Safari浏览器在ios<16.3版本不支持正则表达式零宽断言的解决办法
前端
前端涂涂1 小时前
express查看文件上传报文,处理文件上传,以及formidable包的使用
前端·后端