JS闭包作用域解析
什么是闭包?
简单理解,当在一个函数的外部访问函数内部定义的变量的时候就会形成一个闭包,由这个理解可以知道,当一个函数执行完成的时候,一般情况下,其作用域会被销毁,其内部定义的变量也会变得不可访问,所以闭包打破了这个现象。闭包造成一个函数执行完成之后,其创建的作用域不会被销毁,因为它被函数外部的对象或者变量所引用。由此可知,闭包可以实现作用域的延时存在,但这也会造成内存的泄露。所以在明确知道自己需要使用闭包的时候,采取使用闭包,否则就不要使用闭包。
Eg:
function demo(){
var internal=10;
return function (){
alert(internal);
};
}
demo()();
以上的代码执行之后会弹窗显示10.我们在demo方法的外部打印了其内部的变量,这个就是闭包。
- 作用域产生的原理
我们知道,当执行一个函数的时候,其访问变量的顺序是,首先在其自己的作用域上查询,如果查询不到,则会往其上层作用域来查询,一直查询到最终根作用域(也就是window对象),如果还是查询不到,则会抛出一个undefined的错误。
那么作用域是如何产生的呢?
首先在执行js代码之前,会产生一个全局作用域GO,也就是window对象(后面不再说明),而对于一个函数执行之前会创建自己的作用域AO,并持有其上层函数的作用域,以形成一个作用域链。
作用域创建的基本步骤:
1、 创建一个AO,并持有上层函数的作用域
2、 初始化形参和内部声明的变量(变量声明的提升),并初始化为undefined
3、 将形参和实参相统一
4、 函数定义整体提升
以一下代码为例
function a(){
var a1=0;
function b(){
var b1=1;
function c(){
var c=2;
}
c();
}
b();
}
a();
当js执行之前会创建一个GO
GO{
a:function()…
}
执行a函数的时候会创建a函数的作用域AO
a_AO{
AO_chain:GO,
a1:0,
b:function()…
}
由以上定义可知
b的AO为
b_AO{
AO_Chain:a_AO,
b1:1,
c:function()…
}
c的作用域与此类似,由于此处重点是为了根据作用域来解释闭包的原因,所以这里不再详细的说明作用域,如不了解请自行百度。
- 闭包
eg:有如下一个函数
function demo(){
var arr=[];
for(var i=0;i<10;i++){
arr[i]=function(){
document.write(i+ “ ”);
}
}
return arr;
}
var arr=demo();
for(var j=0;j<10;j++){
arr[j]();
}
熟悉闭包的人都知道最后打印的结果是10个10
10 10 10 10 10 10 10 10 10 10
大家知道这是由于闭包对变量的持有造成的,那么根据作用域的原理是怎么产生的呢?
- GO
GO{
arr = undefined,
demo = function()…
}
- demo 的AO
d_AO{
ao_chain:GO,
arr=[],//用于存放10个函数
i=0
}
- 当执行完成 arr=demo[]之后;
d_AO{
ao_chain:GO,
arr=[function()…,function()…{}],//10个函数,每个函数都是//function(){document.write(i + ‘ ’)}
i=10
}
- 执行arr数组中的函数的时候
a_AO{
ao_chain:d_AO
}
从上面的作用域链可以知道,当数组的函数想要访问i变量的时候,发现它自己的作用域中没有这个变量,所用会通过其作用域链进行查询,然后在d_AO中找到了i对象,此时i对象的值变为了10,所以arr数组中的所有的函数最后打印的结果都是一样的。
为了解决这个问题,可以使用立即执行函数
如下
function demo(){
var arr=[];
for(var i=0;i<10;i++){
(function(j){arr[j]=function(){
document.write(j+ “ ”);
}})(i);
}
return arr;
}
var arr=demo();
for(var j=0;j<10;j++){
arr[j]();
}
结果如图
0 1 2 3 4 5 6 7 8 9
从作用域的解释来看(如果对立即执行函数不了解的,请百度查阅立即执行函数的相关内容)
- GO
GO{
arr = undefined,
demo = function()…
}
- demo 的AO
d_AO{
ao_chain:GO,
arr=[],//用于存放10个函数
i=0
}
前两步与上面的方法创建的作用域相同
第3步开始有些差异
- 执行demo函数的时候
在执行demo函数的时候,因为内部存在一个立即执行函数,所以这个匿名函数会被立即执行,它也会创建自己的作用域
n_AO{
ao_chain:d_AO,
j:n//这里的n保存的是i的当前值,例如第一个n对应的为j:0
}
//对应的arr[j]=function(){document.write(j+ “ ”);},j不会被替换,仍然是保持引用
- 执行arr函数的时候
arr_AO{
ao_chain:n_AO//例如arr[1]对应的就是ao_chain:1_AO
}
从上面的作用域链可以看出,当执行arr中的函数的时候,例如执行arr[2]的时候,首先到自己的作用域查询j变量,发现找不到j,于是沿着作用域链进行查找,首先查询2_AO,在2_AO中找到了j变量,此时j变量的值为2,于是打印的结果就为2,这样我们就实现了我们得需要。
以上解释若有错误,还望指点,包涵。谢谢!
JS闭包作用域解析的更多相关文章
- js闭包的作用域以及闭包案列的介绍:
转载▼ 标签: it js闭包的作用域以及闭包案列的介绍: 首先我们根据前面的介绍来分析js闭包有什么作用,他会给我们编程带来什么好处? 闭包是为了更方便我们在处理js函数的时候会遇到以下的几 ...
- 大部分人都会做错的经典JS闭包面试题
由工作中演变而来的面试题 这是一个我工作当中的遇到的一个问题,似乎很有趣,就当做了一道题去面试,发现几乎没人能全部答对并说出原因,遂拿出来聊一聊吧. 先看题目代码: function fun(n,o) ...
- Js闭包常见三种用法
Js闭包特性源于内部函数可以将外部函数的活动对象保存在自己的作用域链上,所以使内部函数的可以将外部函数的活动对象占为己有,可以在外部函数销毁时依然存有外部函数内的活动对象内容,这样做的好处是可 ...
- js闭包之初步理解( JavaScript closure)
闭包一直是js中一个比较难于理解的东西,而平时用途又非常多,因此不得不对闭包进行必要的理解,现在来说说我对js闭包的理解. 要理解闭包,肯定是要先了解js的一个重要特性, 回想一下,那就是函数作用域, ...
- (原创)JS闭包看代码理解
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="C ...
- js闭包理解
js闭包的作用是使函数外可以访问函数内部的变量,是通过 在函数内部 定义 访问函数内变量 的函数实现的,内部的一个函数产生一个闭包 function a() { var i=0; return fun ...
- js闭包理解实例小结
Js闭包 闭包前要了解的知识 1. 函数作用域 (1).Js语言特殊之处在于函数内部可以直接读取全局变量 <script type="text/javascript"> ...
- Js闭包的用途
本来想总结一点JavaScript中的闭包的一些用法,在查资料的时候发现了一篇很好的文章,就转过来收藏了,下面附上传送门: js闭包的用途 ---------sunlylorn 我们来看看闭包的用途. ...
- js闭包和ie内存泄露原理
也议 js闭包和ie内存泄露原理 可以, 但小心使用. 闭包也许是 JS 中最有用的特性了. 有一份比较好的介绍闭包原理的文档. 有一点需要牢记, 闭包保留了一个指向它封闭作用域的指针, 所以, 在给 ...
随机推荐
- (转)protein 数据库
最早关注蛋白质互作网络,是在来GDMC第一年的时候,中间停了半年看互作-各种算法,网络分析停滞不前,没想到搞到最后,还是和网络碰到了一起,我总是会潜意识走近给自己第一印象不错的object,包括人.用 ...
- Redis安装、命令以及设置密码遇到的问题
一.下载Redis 如果没有 安装wget先安装wget和gcc(使用make的时候会用上) wget http://download.redis.io/releases/redis-4.0.8.ta ...
- 2018申请淘宝客AppKey
1.www.alimama.com 申请账号进入后2.进入我的联盟,按下面的步骤 完成以后等待网站审核. 3.审核完成后 按以下的图,申请进入开放平台或得appkey 4.最后就可以进入开放平台申请看 ...
- python Cookie Session 相关用法
Cookie一.前言1.http协议是无状态的. 无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响 应情况直接影响,也不会直接影响后面的请 ...
- 整理4种Vue组件通信方式
整理4种Vue组件通信方式 重点是梳理了前两个,父子组件通信和eventBus通信,我觉得Vue文档里的说明还是有一些简易,我自己第一遍是没看明白. 父子组件的通信 非父子组件的eventBus通信 ...
- [Swift]LeetCode356. 直线对称 $ Line Reflection
Given n points on a 2D plane, find if there is such a line parallel to y-axis that reflect the given ...
- Java中的方法(形参及实参)return返回类型
如何定义 Java 中的方法 所谓方法,就是用来解决一类问题的代码的有序组合,是一个功能模块. 一般情况下,定义一个方法的语法是: 其中: 1. 访问修饰符:方法允许被访问的权限范围, 可以是 pub ...
- Git使用基础介绍
git教程:一.git的简介: -git是分布式版本控制系统由Linus为Linux用C语言写的. -什么是集中式版本控制系统: 版本库是集中存放在中央服务器,干活的时候用自己的电 ...
- 用Flutter开发的跨平台项目,完美运行在Android和IOS上,Material简洁风格,包括启动页、引导页、注册、登录、首页、体系、公众号、导航、项目,还有漂亮的妹子图库,运行极度流畅,结构清晰,代码规范,值得拥有
Flutter学习资源汇总持续更新中...... Flutter官方网站 Flutter中文网 wendux的Flutter实战 Flutter官方exampleflutter_gallery 阿里巴 ...
- Python 创建递归文件夹
# 创建递归文件夹 def createfiles(filepathname): try: os.makedirs(filepathname) except Exception as err: pri ...