什么是闭包?

简单理解,当在一个函数的外部访问函数内部定义的变量的时候就会形成一个闭包,由这个理解可以知道,当一个函数执行完成的时候,一般情况下,其作用域会被销毁,其内部定义的变量也会变得不可访问,所以闭包打破了这个现象。闭包造成一个函数执行完成之后,其创建的作用域不会被销毁,因为它被函数外部的对象或者变量所引用。由此可知,闭包可以实现作用域的延时存在,但这也会造成内存的泄露。所以在明确知道自己需要使用闭包的时候,采取使用闭包,否则就不要使用闭包。

Eg:

function demo(){

var internal=10;

return function (){

alert(internal);

};

}

demo()();

以上的代码执行之后会弹窗显示10.我们在demo方法的外部打印了其内部的变量,这个就是闭包。

  1. 作用域产生的原理

我们知道,当执行一个函数的时候,其访问变量的顺序是,首先在其自己的作用域上查询,如果查询不到,则会往其上层作用域来查询,一直查询到最终根作用域(也就是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的作用域与此类似,由于此处重点是为了根据作用域来解释闭包的原因,所以这里不再详细的说明作用域,如不了解请自行百度。

  1. 闭包

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

大家知道这是由于闭包对变量的持有造成的,那么根据作用域的原理是怎么产生的呢?

  1. GO

GO{

arr = undefined,

demo = function()…

}

  1. demo 的AO

d_AO{

ao_chain:GO,

arr=[],//用于存放10个函数

i=0

}

  1. 当执行完成 arr=demo[]之后;

d_AO{

ao_chain:GO,

arr=[function()…,function()…{}],//10个函数,每个函数都是//function(){document.write(i + ‘ ’)}

i=10

}

  1. 执行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

从作用域的解释来看(如果对立即执行函数不了解的,请百度查阅立即执行函数的相关内容)

  1. GO

GO{

arr = undefined,

demo = function()…

}

  1. demo 的AO

d_AO{

ao_chain:GO,

arr=[],//用于存放10个函数

i=0

}

前两步与上面的方法创建的作用域相同

第3步开始有些差异

  1. 执行demo函数的时候

在执行demo函数的时候,因为内部存在一个立即执行函数,所以这个匿名函数会被立即执行,它也会创建自己的作用域

n_AO{

ao_chain:d_AO,

j:n//这里的n保存的是i的当前值,例如第一个n对应的为j:0

}

//对应的arr[j]=function(){document.write(j+ “ ”);},j不会被替换,仍然是保持引用

  1. 执行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闭包作用域解析的更多相关文章

  1. js闭包的作用域以及闭包案列的介绍:

    转载▼ 标签: it   js闭包的作用域以及闭包案列的介绍:   首先我们根据前面的介绍来分析js闭包有什么作用,他会给我们编程带来什么好处? 闭包是为了更方便我们在处理js函数的时候会遇到以下的几 ...

  2. 大部分人都会做错的经典JS闭包面试题

    由工作中演变而来的面试题 这是一个我工作当中的遇到的一个问题,似乎很有趣,就当做了一道题去面试,发现几乎没人能全部答对并说出原因,遂拿出来聊一聊吧. 先看题目代码: function fun(n,o) ...

  3. Js闭包常见三种用法

        Js闭包特性源于内部函数可以将外部函数的活动对象保存在自己的作用域链上,所以使内部函数的可以将外部函数的活动对象占为己有,可以在外部函数销毁时依然存有外部函数内的活动对象内容,这样做的好处是可 ...

  4. js闭包之初步理解( JavaScript closure)

    闭包一直是js中一个比较难于理解的东西,而平时用途又非常多,因此不得不对闭包进行必要的理解,现在来说说我对js闭包的理解. 要理解闭包,肯定是要先了解js的一个重要特性, 回想一下,那就是函数作用域, ...

  5. (原创)JS闭包看代码理解

    <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="C ...

  6. js闭包理解

    js闭包的作用是使函数外可以访问函数内部的变量,是通过 在函数内部 定义 访问函数内变量 的函数实现的,内部的一个函数产生一个闭包 function a() { var i=0; return fun ...

  7. js闭包理解实例小结

    Js闭包 闭包前要了解的知识  1. 函数作用域 (1).Js语言特殊之处在于函数内部可以直接读取全局变量 <script type="text/javascript"> ...

  8. Js闭包的用途

    本来想总结一点JavaScript中的闭包的一些用法,在查资料的时候发现了一篇很好的文章,就转过来收藏了,下面附上传送门: js闭包的用途 ---------sunlylorn 我们来看看闭包的用途. ...

  9. js闭包和ie内存泄露原理

    也议 js闭包和ie内存泄露原理 可以, 但小心使用. 闭包也许是 JS 中最有用的特性了. 有一份比较好的介绍闭包原理的文档. 有一点需要牢记, 闭包保留了一个指向它封闭作用域的指针, 所以, 在给 ...

随机推荐

  1. python中的单向链表实现

    引子 数据结构指的是是数据的组织的方式.从单个数据到一维结构(线性表),二维结构(树),三维结构(图),都是组织数据的不同方式. 为什么需要链表? 顺序表的构建需要预先知道数据大小来申请连续的存储空间 ...

  2. HTTP Status 500 - Request processing failed; nested exception is org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)

    今天整合ssm框架 时 遇到的问题 困扰我好长时间     原因就是  mapper文件 没有被加载进来 但是 为什么没有被加载进来呢  因为中间的配置文件出了一些问题 网上大多数说法是   在pom ...

  3. oracle的知识点总结

    体系结构:数据库的体系结构是指数据库的组成.工作过程与原理,以及数据在数据库中的组织与管理机制.体系结构包括:实例(instence),数据库文件(database),用户进程(user proces ...

  4. Java-JSON 解析

    JSON  JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式.它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采 ...

  5. 报错Domain=NSCocoaErrorDomain Code=3840 "Garbage at end."

    网络请求出现报错:Domain=NSCocoaErrorDomain Code=3840 "Garbage at end." 出现的问题是后台返回了两次json数据!

  6. return和throw某些特性相似

    拷贝构造函数的调用拷贝构造函数会在以下三中情况下被调用(1)当类的一个对象去初始化该类的另一个对象时 int main(){ Point a(1,2); Point b(a);//用对象a初始化对象b ...

  7. 欢迎访问我的独立博客 tracefact.net (2019.1.30)

    欢迎访问我的独立博客 tracefact.net 长期以来,我都同时维护着两个博客,博客园和 tracefact.net,感觉有点分散精力,所以博客园以后不再每篇文章都同步更新了. 我会挑个别比较好的 ...

  8. TCP协议学习总结(上)

    在计算机领域,数据的本质无非0和1,创造0和1的固然伟大,但真正百花齐放的还是基于0和1之上的各种层次之间的组合(数据结构)所带给我们人类各种各样的可能性.例如TCP协议,我们的生活无不无时无刻的站在 ...

  9. 升讯威微信营销系统开发实践:微信接口的 .NET 封装

    GitHub:https://github.com/iccb1013/Sheng.WeixinConstruction因为个人精力时间有限,不会再对现有代码进行更新维护,不过微信接口比较稳定,经测试至 ...

  10. 安装与配置Flutter开发环境

    这篇博客我们介绍了Flutter,并且对比了H5,React Native,Flutter. 由于Flutter是跨平台的开发框架,开发一次可以同时运行在Android和iOS上面,所以我们开发时最好 ...