We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
从《JS 总结之函数、作用域链》一文中知道作用域链的作用,保证了对所有变量对象的有序访问。
函数外的是无法访问函数内部的变量,有时候要用到怎么办?我们的主角,闭包就是可以解决这个问题。
引用 MDN 上的解释:
闭包是函数和声明该函数的词法环境的组合。
引用 《JavaScript 高级程序设计(第 3 版)》上的解释:
闭包是指有权访问另一个函数作用域中的变量的函数。
这两个解释都在说着同一件事,闭包能访问声明时函数所在的环境中的变量和函数。
那具体是因为什么才会让闭包访问到环境中的变量和函数,这得靠两兄弟:变量对象和作用域链。
变量对象
当执行函数的时候,会创建和初始化一个函数执行环境,在函数执行环境中,全局执行环境的变量对象(Variable Object,缩写为 VO)不能直接访问,此时由激活对象(Activation Object,缩写为 AO)扮演 VO 的角色。
变量对象专门用来梳理和记住这些变量和函数,是作用域链形成的前置条件 。但我们无法直接使用这个变量对象,该对象主要是给 JS 引擎使用的。具体可以查看《JS 总结之变量对象》。
变量对象就相当于一个存放的仓库,获取到里面的东西,还得需要去获取这些的路径,这就是作用域链的事情了。
作用域链
然而,光有变量对象可完成不了闭包的形成,怎样才能让函数访问到,这得靠作用域链,作用域链的作用就是让函数找到变量对象里面的变量和函数。具体可以查看《JS 总结之函数、作用域链》
虽然都是讲闭包,但 MDN 上面讲的是声明该函数的词法环境,而 JS 高程讲的是访问另一个函数作用域中,从解释上的不同,闭包便有了理论中的闭包( MDN )和实践中的闭包( JS 高程)之分。
根据 MDN 的解释写个例子:
var a = 1 function fn() { console.log(a) } fn()
函数 fn 和函数 fn 的词法作用域构成了一个闭包。但是这不是普通的函数吗?
在《JavaScript 权威指南》中也得到证实:
从技术的角度讲,所有 JavaScript 函数都是闭包
汤姆大叔翻译的文章中讲,实践中的闭包需要满足以下两个条件:
什么是上下文?即函数的执行环境。
什么是自由变量?即函数的词法作用域中的变量和函数,而不是函数本身的参数或者局部变量,或者说是所在函数的变量对象中的变量和函数。
这两点和 JS 高程中讲的闭包的解释不谋而合。
现在写个符合的例子:
function fn() { var a = 1 function fn1() { console.log(a) } return fn1 } var b = fn() b()
当执行 b 的时候,创建它的执行环境 fn 早已经摧毁,但函数 b 还能访问到变量 a。
好吧,我有点乱!
要彻底明白这个是咋回事,要结合执行环境、活动变量和作用域链来看,让我们来看看这个例子的执行过程:
环境栈 = [globalContext]
globalContext = { VO: [global], Scope: [globalContext.VO], this: globalContext.VO }
全局执行环境的作用域链
[[scope]]
fn.[[scope]] = [ globalContext.VO ]
环境栈 = [fnContext, globalContext]
fnContext = { AO: { arguments: {}, scope: undefined, fn1: reference to function fn1(){} }, Scope: [AO, globalContext.VO], this: undefined }
fnContext 的作用域链
fn1.[[scope]] = [ fnContext.AO, globalContext.VO ]
环境栈 = [ fn1Context, globalContext ]
fn1Context = { AO: { arguments: {} }, Scope: [AO, fnContext.AO, globalContext.VO], this: undefined }
当执行函数 b 的时候,创建它的执行环境 fn 早已摧毁(步骤 6),只留下了它的活动变量 fnContext.AO 于内存中(步骤 5):
fnContext.AO
fn1Context = { Scope: [AO, fnContext.AO, globalContext.VO] }
fnContext.AO 存在 fn1 的作用域链中,所以能访问到fn1 的词法环境,这便形成了闭包。因此,闭包是变量对象和作用域链共同作用的结果。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
从《JS 总结之函数、作用域链》一文中知道作用域链的作用,保证了对所有变量对象的有序访问。
👩🎨 问题
函数外的是无法访问函数内部的变量,有时候要用到怎么办?我们的主角,闭包就是可以解决这个问题。
👷 什么是闭包
引用 MDN 上的解释:
引用 《JavaScript 高级程序设计(第 3 版)》上的解释:
🐳 相同点
这两个解释都在说着同一件事,闭包能访问声明时函数所在的环境中的变量和函数。
那具体是因为什么才会让闭包访问到环境中的变量和函数,这得靠两兄弟:变量对象和作用域链。
变量对象
当执行函数的时候,会创建和初始化一个函数执行环境,在函数执行环境中,全局执行环境的变量对象(Variable Object,缩写为 VO)不能直接访问,此时由激活对象(Activation Object,缩写为 AO)扮演 VO 的角色。
变量对象专门用来梳理和记住这些变量和函数,是作用域链形成的前置条件 。但我们无法直接使用这个变量对象,该对象主要是给 JS 引擎使用的。具体可以查看《JS 总结之变量对象》。
变量对象就相当于一个存放的仓库,获取到里面的东西,还得需要去获取这些的路径,这就是作用域链的事情了。
作用域链
然而,光有变量对象可完成不了闭包的形成,怎样才能让函数访问到,这得靠作用域链,作用域链的作用就是让函数找到变量对象里面的变量和函数。具体可以查看《JS 总结之函数、作用域链》
🐬 不同点
虽然都是讲闭包,但 MDN 上面讲的是声明该函数的词法环境,而 JS 高程讲的是访问另一个函数作用域中,从解释上的不同,闭包便有了理论中的闭包( MDN )和实践中的闭包( JS 高程)之分。
🕵 理论中的闭包
根据 MDN 的解释写个例子:
函数 fn 和函数 fn 的词法作用域构成了一个闭包。但是这不是普通的函数吗?
在《JavaScript 权威指南》中也得到证实:
👨💻 实践中的闭包
汤姆大叔翻译的文章中讲,实践中的闭包需要满足以下两个条件:
什么是上下文?即函数的执行环境。
什么是自由变量?即函数的词法作用域中的变量和函数,而不是函数本身的参数或者局部变量,或者说是所在函数的变量对象中的变量和函数。
这两点和 JS 高程中讲的闭包的解释不谋而合。
现在写个符合的例子:
当执行 b 的时候,创建它的执行环境 fn 早已经摧毁,但函数 b 还能访问到变量 a。
好吧,我有点乱!
要彻底明白这个是咋回事,要结合执行环境、活动变量和作用域链来看,让我们来看看这个例子的执行过程:
🍑 执行过程
全局执行环境的作用域链
到 fn 的[[scope]]
属性[[scope]]
创建 fn 作用域链fnContext 的作用域链
到 fn1 的[[scope]]
属性[[scope]]
创建 fn1 作用域链🍓 柳暗花明
当执行函数 b 的时候,创建它的执行环境 fn 早已摧毁(步骤 6),只留下了它的活动变量
fnContext.AO
于内存中(步骤 5):fnContext.AO
存在 fn1 的作用域链中,所以能访问到fn1 的词法环境,这便形成了闭包。因此,闭包是变量对象和作用域链共同作用的结果。🚀 参考
The text was updated successfully, but these errors were encountered: