【发布时间】:2022-08-20 23:04:59
【问题描述】:
我试图理解 JavaScript 中的 arrow functions 并且对它们如何与 ExecutionContext/environment 和闭包交互有一些疑问。
我如何理解模型:
据我所知,JS 中的“规范”模型是,随着代码的执行,会维护ExecutionContexts 的堆栈(1、6)。 IE。一开始有一个全局的ExecutionContext,当一个函数被调用时,一个新的ExecutionContext在它的执行时被添加,当它完成时,它被弹出。 IE。它匹配frames on callstack。
假设稍微简化一点(忽略 global/function/eval 和没有 let 和 const 之间的差异(即 variable environment),ExecutionContext 由 LexicalEnvironemnt 组成,而 LexicalEnvironemnt 又由三个组件组成:
- 环境记录:变量/函数符号和它们所代表的对象之间的映射。
-
参考外部环境: 引用外部词汇
ExecutionContext -
这个绑定:什么
this变量引用。对于未绑定的函数,这是根据方法的调用方式设置的 (2)当一个函数被调用时,一个新的
ExecutionContext在其执行期间被创建(跟踪它的变量,因为它们在环境记录, ...)。正常功能
正常功能,在词法范围内:
对于正常功能,s.a.
b()在下面的例子中,创建新的ExecutionContext比较简单。function a() { var myVar = 42; function b() { console.log(myVar) console.log(this) } b() } a()- 环境记录:对所有类型来说总是很简单,只需扫描方法,注意所有符号,初始化为默认值。
- 对外部环境的引用:我们在其词法外部范围内运行该方法,即我们可以简单地引用执行堆栈 (3) 上当前的
EnvironmentContext(即a()) .这使我们可以访问外部词法范围变量myVar。 - 它被正常调用,所以我们会为此使用全局绑定,即在浏览器中的一个窗口。
正常功能,超出词法范围:
function a() { let myVar = 42; function b() { console.log(myVar) // from closure console.log(myCVar) // will not be accessible, even if it will have lived in above frame (from c) console.log(this) } return b } function c(f) { let myVar = 48; let myCVar = 49; f() } returnedFun = a() c(returnedFun)在这种情况下,当我们运行方法
b(作为方法c内的f(),从a返回后),就没有那么简单了。新的ExecutionContext的1)和3)部分仍然填充相同,但2)必须不同。在
b从其词法范围返回时,即从函数a,必须在当前ExecutionContext之外创建一个闭包(a()正在执行,myVar: 42在环境记录) 并添加到返回的函数对象b。当函数对象在函数
c(f())中执行时,而不是连线新创建的ExecutionContext\'s参考外部环境对于执行堆栈顶部的那个(即当前执行的c()),必须使用函数对象f(返回函数b)的闭包。IE。这参考外部环境对于刚刚创建的
ExecutionContext刚刚执行的f()不指向当前正在运行的函数的ExecutionContext(即运行时外部范围;将是c()),而是指向捕获的闭包不再运行的词法外部环境 (a())。这个捕获的闭包是可见的?伪?
returnedFun对象 (.[[Scopes]][0].myVar == 42) 的console.dir时的属性。正常功能,有界
let myObj = {asdf: 42} function a() { console.write(\"tst\");} console.dir(a.bind(myObj))同样,当显式使用
bind时 - args/this 被添加到函数对象中,以 ?pseudo?财产[[BoundThis]]。当函数对象被调用并创建相应的ExecutionContext以填充其这个绑定.箭头函数
但是箭头函数呢?根据我的谷歌搜索,解释它们的一种常见方法是它们没有得到自己的
ExecutionContext(4,5),而是重新使用它们的词汇外部范围之一;但这是如何工作的,真的吗?词法范围内的箭头函数:
function a() { let myVar = 42; b = () => { var myBVar = 48; } b() console.log(myBVar) // not accessible -> run of b() must use copy of a\'s EC } a()当箭头函数在其词法范围内执行时,它 - 再次 - 相对简单。当函数
b()被执行时,当前的ExecutionContext(对于a,这是b\的词法外部范围)被复制(需要允许只有它自己的变量,否则在a()你可以访问myBVar) 并使用;包含这个绑定(由下面明确的binding 示例演示)。function a() { console.log(this) arrF = () => { console.log(this.myMyObjVar) } arrF() // when called duplicates current ExecutionContext (LexicalEnvironment + thisBinding), runs in it. } var myObj = {myMyObjVar: 42} a.bind(myObj)()箭头函数,超出词法范围
但是如果箭头函数超出了它的词法范围呢? IE。它需要创建闭包吗?
function a() { console.log(this) var asdf = 48; arrF = () => { console.log(this.myMyObjVar) console.log(asdf) } return arrF } var myObj = {myMyObjVar: 42} aBound = a.bind(myObj) returnedArrF = aBound() returnedArrF() console.dir(returnedArrF)在这种情况下,
returnedArrF\ 的闭包不仅需要包含环境记录a()\'sExecutionContext(提供对来自外部词法范围 (asdf) 的变量的正常闭包访问),即 Chromium Devtools 向我们展示的[[Scopes]],但也对其这个绑定.即需要保存几乎整个ExecutionContext,以允许 excaped 箭头函数 - 在执行时 - 不需要拥有自己的并重用其外部词法范围。奇怪的是,存储这个绑定似乎没有作为?伪浮出水面?
console.dir可见的属性,与bind\'edthis或正常关闭的方式相同。我有什么问题?
- 是对外部词汇上下文
ExecutionContext的引用,具体来说这个绑定对于箭头函数,使用与闭包类似的机制(在类似模型下)存储(想想[[scopes]],就像 chrome 开发工具显示的那样)是?- 如果是这种情况,为什么
thisBinding由bind(...)创建,正常闭包在Chrome devtools/console.dir中可见,但箭头函数这个绑定不是吗?它只是实现细节还是有一些更高层次的原因?
- 为什么在检查时
bind\'ed 函数和箭头函数的外观会存在差异(或者它只是实现细节而不是 JS 模型要求的内容?)? - 我的模型对吗?
什么不是我的问题/笔记?
我知道
ExecutionContext等只是一个规范“模型”,而不是单个虚拟机(V8,...)如何实现 JS。我也了解 Chromium devtools 可能会显示在对象上并不真正存在/可访问的“伪”属性(s.a.[[Scopes]])。我也对箭头函数如何表现以及如何使用它们不感兴趣(我认为我掌握得很好;但是如果您认为我根据我的示例遗漏了一些东西 - 请随时告诉我)。
相反,我很好奇规范“模型”如何映射到实际实现。我希望从问题中可以清楚地看到????。
笔记:
我试图阅读以理解这一点的内容:
- https://betterprogramming.pub/javascript-internals-execution-context-bdeee6986b3b#:~:text=There%20are%20three%20types%20of,in%20which%20code%20is%20executed
- https://blog.bitsrc.io/understanding-execution-context-and-execution-stack-in-javascript-1c9ea8642dd0
- https://betterprogramming.pub/execution-context-lexical-environment-and-closures-in-javascript-b57c979341a5
- https://medium.com/front-end-weekly/the-strange-case-of-arrow-functions-and-mr-3087a0d7b71f
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
- 如果是这种情况,为什么
- 是对外部词汇上下文
-
\"我的问题是什么?\"一个明确的迹象表明这个问题\"需要更多的关注\"
-
\"一种常见的解释方式是 [箭头函数] 没有自己的 ExecutionContext\" - 不,这是错误的,并且不受您提供的引用的支持。箭头函数在调用它们时通常创建的执行上下文中不会得到自己的 [[ThisBinding]]。
-
\"检查时显式绑定函数和箭头函数的外观差异\" - 这绝对是您正在使用的调试器的实现细节。它不是由任何东西强制要求的(当然,它或多或少地受到规范中描述的心理模型的影响)。
-
顺便说一句,你的心智模型\"正常功能,在词法范围内\" 太复杂了。这已经被一般的\"任何范围内的正常函数\"案例所涵盖。\"在
b从其词法范围返回时,必须创建一个闭包\" 是错误的。在定义(创建)函数时已经正确创建了闭包,因为每一个函数,无论以后该函数会发生什么。
标签: javascript closures executioncontext