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
本期的主题是作用域闭包,本计划一共28期,每期重点攻克一个面试重难点,如果你还不了解本进阶计划,文末点击查看全部文章。
如果觉得本系列不错,欢迎点赞、评论、转发,您的支持就是我坚持的最大动力。
红宝书(p178)上对于闭包的定义:闭包是指有权访问另外一个函数作用域中的变量的函数,
MDN 对闭包的定义为:闭包是指那些能够访问自由变量的函数。
其中自由变量,指在函数中使用的,但既不是函数参数arguments也不是函数的局部变量的变量,其实就是另外一个函数作用域中的变量。
arguments
使用上一篇文章的例子来说明下自由变量
function getOuter(){ var date = '1127'; function getDate(str){ console.log(str + date); //访问外部的date } return getDate('今天是:'); //"今天是:1127" } getOuter();
其中date既不是参数arguments,也不是局部变量,所以date是自由变量。
date
总结起来就是下面两点:
首先来一个简单的例子
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f; } var foo = checkscope(); // foo指向函数f foo(); // 调用函数f()
简要的执行过程如下:
进入全局代码,创建全局执行上下文,全局执行上下文压入执行上下文栈
全局执行上下文初始化
执行 checkscope 函数,创建 checkscope 函数执行上下文,checkscope 执行上下文被压入执行上下文栈
checkscope 执行上下文初始化,创建变量对象、作用域链、this等
checkscope 函数执行完毕,checkscope 执行上下文从执行上下文栈中弹出
执行 f 函数,创建 f 函数执行上下文,f 执行上下文被压入执行上下文栈
f 执行上下文初始化,创建变量对象、作用域链、this等
f 函数执行完毕,f 函数上下文从执行上下文栈中弹出
那么问题来了, 函数f 执行的时候,checkscope 函数上下文已经被销毁了,那函数f是如何获取到scope变量的呢?
上文(【进阶2-1期】深入浅出图解作用域链和闭包)介绍过,函数f 执行上下文维护了一个作用域链,会指向指向checkscope作用域,作用域链是一个数组,结构如下。
checkscope
fContext = { Scope: [AO, checkscopeContext.AO, globalContext.VO], }
所以指向关系是当前作用域 --> checkscope作用域--> 全局作用域,即使 checkscopeContext 被销毁了,但是 JavaScript 依然会让 checkscopeContext.AO(活动对象) 活在内存中,f 函数依然可以通过 f 函数的作用域链找到它,这就是闭包实现的关键。
上面介绍的是实践角度,其实闭包有很多种介绍,说法不一。
汤姆大叔翻译的关于闭包的文章中的定义,ECMAScript中,闭包指的是:
1、从理论角度:所有的函数。因为它们都在创建的时候就将上层上下文的数据保存起来了。哪怕是简单的全局变量也是如此,因为函数中访问全局变量就相当于是在访问自由变量,这个时候使用最外层的作用域。
2、从实践角度:以下函数才算是闭包:
var data = []; for (var i = 0; i < 3; i++) { data[i] = function () { console.log(i); }; } data[0](); data[1](); data[2]();
如果知道闭包的,答案就很明显了,都是3
循环结束后,全局执行上下文的VO是
globalContext = { VO: { data: [...], i: 3 } }
执行 data[0] 函数的时候,data[0] 函数的作用域链为:
data[0]Context = { Scope: [AO, globalContext.VO] }
由于其自身没有i变量,就会向上查找,所有从全局上下文查找到i为3,data[1] 和 data[2] 是一样的。
改成闭包,方法就是data[i]返回一个函数,并访问变量i
data[i]
i
var data = []; for (var i = 0; i < 3; i++) { data[i] = (function (i) { return function(){ console.log(i); } })(i); } data[0](); // 0 data[1](); // 1 data[2](); // 2
循环结束后的全局执行上下文没有变化。
执行 data[0] 函数的时候,data[0] 函数的作用域链发生了改变:
data[0]Context = { Scope: [AO, 匿名函数Context.AO, globalContext.VO] }
匿名函数执行上下文的AO为:
匿名函数Context = { AO: { arguments: { 0: 0, length: 1 }, i: 0 } }
因为闭包执行上下文中贮存了变量i,所以根据作用域链会在globalContext.VO中查找到变量i,并输出0。
globalContext.VO
上面必刷题改动一个地方,把for循环中的var i = 0,改成let i = 0。结果是什么,为什么???
var i = 0
let i = 0
var data = []; for (let i = 0; i < 3; i++) { data[i] = function () { console.log(i); }; } data[0](); data[1](); data[2]();
JavaScript深入之闭包
进阶系列文章汇总:https://github.com/yygmind/blog,内有优质前端资料,欢迎领取,觉得不错点个star。
我是木易杨,网易高级前端工程师,跟着我每周重点攻克一个前端面试重难点。接下来让我带你走进高级前端的世界,在进阶的路上,共勉!
The text was updated successfully, but these errors were encountered:
难道不是 匿名函数Context.AO
Sorry, something went wrong.
文中的执行上下文顺序图,貌似不正确吧。** checkscope** 函数的执行上下文应该出栈后,f 函数的执行上下文才会入栈吧,建议修改一下。
不知道我的理解对不对,我有一个疑问,图中红色的框:
好像之前理解确实错了,这是根据新的理解画出来的图,以及新的疑问:
(这块好复杂 😂)
No branches or pull requests
本期的主题是作用域闭包,本计划一共28期,每期重点攻克一个面试重难点,如果你还不了解本进阶计划,文末点击查看全部文章。
如果觉得本系列不错,欢迎点赞、评论、转发,您的支持就是我坚持的最大动力。
红宝书(p178)上对于闭包的定义:闭包是指有权访问另外一个函数作用域中的变量的函数,
MDN 对闭包的定义为:闭包是指那些能够访问自由变量的函数。
其中自由变量,指在函数中使用的,但既不是函数参数
arguments
也不是函数的局部变量的变量,其实就是另外一个函数作用域中的变量。使用上一篇文章的例子来说明下自由变量
其中
date
既不是参数arguments
,也不是局部变量,所以date
是自由变量。总结起来就是下面两点:
分析
首先来一个简单的例子
简要的执行过程如下:
进入全局代码,创建全局执行上下文,全局执行上下文压入执行上下文栈
全局执行上下文初始化
执行 checkscope 函数,创建 checkscope 函数执行上下文,checkscope 执行上下文被压入执行上下文栈
checkscope 执行上下文初始化,创建变量对象、作用域链、this等
checkscope 函数执行完毕,checkscope 执行上下文从执行上下文栈中弹出
执行 f 函数,创建 f 函数执行上下文,f 执行上下文被压入执行上下文栈
f 执行上下文初始化,创建变量对象、作用域链、this等
f 函数执行完毕,f 函数上下文从执行上下文栈中弹出
那么问题来了, 函数f 执行的时候,checkscope 函数上下文已经被销毁了,那函数f是如何获取到scope变量的呢?
上文(【进阶2-1期】深入浅出图解作用域链和闭包)介绍过,函数f 执行上下文维护了一个作用域链,会指向指向
checkscope
作用域,作用域链是一个数组,结构如下。所以指向关系是当前作用域 -->
checkscope
作用域--> 全局作用域,即使 checkscopeContext 被销毁了,但是 JavaScript 依然会让 checkscopeContext.AO(活动对象) 活在内存中,f 函数依然可以通过 f 函数的作用域链找到它,这就是闭包实现的关键。概念
上面介绍的是实践角度,其实闭包有很多种介绍,说法不一。
汤姆大叔翻译的关于闭包的文章中的定义,ECMAScript中,闭包指的是:
1、从理论角度:所有的函数。因为它们都在创建的时候就将上层上下文的数据保存起来了。哪怕是简单的全局变量也是如此,因为函数中访问全局变量就相当于是在访问自由变量,这个时候使用最外层的作用域。
2、从实践角度:以下函数才算是闭包:
面试必刷题
如果知道闭包的,答案就很明显了,都是3
循环结束后,全局执行上下文的VO是
执行 data[0] 函数的时候,data[0] 函数的作用域链为:
由于其自身没有i变量,就会向上查找,所有从全局上下文查找到i为3,data[1] 和 data[2] 是一样的。
解决办法
改成闭包,方法就是
data[i]
返回一个函数,并访问变量i
循环结束后的全局执行上下文没有变化。
执行 data[0] 函数的时候,data[0] 函数的作用域链发生了改变:
匿名函数执行上下文的AO为:
因为闭包执行上下文中贮存了变量
i
,所以根据作用域链会在globalContext.VO
中查找到变量i
,并输出0。思考题
上面必刷题改动一个地方,把for循环中的
var i = 0
,改成let i = 0
。结果是什么,为什么???参考
进阶系列目录
交流
进阶系列文章汇总:https://github.com/yygmind/blog,内有优质前端资料,欢迎领取,觉得不错点个star。
我是木易杨,网易高级前端工程师,跟着我每周重点攻克一个前端面试重难点。接下来让我带你走进高级前端的世界,在进阶的路上,共勉!
The text was updated successfully, but these errors were encountered: