什么是闭包?

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

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. Jquery weui picker 支持label和value

    万年没更新了. 最近用jquery weui. 在使用picker时需要一些问题. 就是让picker 显示label, 但是取值的时候取value用于存储. 官网例子如下 Jquery-weui 官 ...

  2. vbs编写一个函数,将1001到1050(50串数字)读入test.txt文件。每串数字占一行,不是覆盖。

    Option Explicit---------------------------------------------------------开头 dim fas,objfso,printstr,o ...

  3. c++标准---for循环新用法

    贪吃蛇游戏的c++版本,从GitHub上clone下来的,在startinterface.cpp文件中有如下代码: void   StartInterface::PrintText() { for ( ...

  4. [Lyft Level 5 Challenge 2018 - Elimination Round][Codeforces 1033D. Divisors]

    题目链接:1033D - Divisors 题目大意:给定\(n\)个数\(a_i\),每个数的约数个数为3到5个,求\(\prod_{i=1}^{n}a_i\)的约数个数.其中\(1 \leq n ...

  5. Cmd命令 查看端口被占用

    1)第一步 打开cmd命令窗口,输入命令:netstat -ano|findstr 输入端口号 2)第二步 继续输入命令:tasklist|findstr  第一步查询到的进程号 3)第三步 根据第二 ...

  6. swust oj 1014

    交换排序算法的设计与实现——冒泡排序 1000(ms) 10000(kb) 2640 / 7503 编程实现冒泡排序,按照非递减排序,测试数据为整数. 输入 第一行是待排序数据元素的个数: 第二行是待 ...

  7. error: can't copy 'docx\templates\default-docx-template': doesn't exist or not a regular file --------------- Failed building wheel for python-docx; python-docx的安装使用;python操作word

    本人第一安装python-docx很不幸就出现了,如下的错误:(如果你也遇到同样的错误,不要慌可以参考下面解决方案,由于第一次处理这种错误,如有不对欢迎大家多多批评指正) 问题所在是因为我们的setu ...

  8. javabean简介

    Javabean简介 JavaBean是一个可重复使用的软件组件.实际上JavaBean是一种Java类,通过封装属性和方法成为具有某种功能或者处理某个业务的对象,简称bean.由于javabean是 ...

  9. linux下SS 网络命令详解

    ss命令用来显示处于活动状态的套接字信息. ss命令可以用来获取socket统计信息,它可以显示和netstat类似的内容. 但ss的优势在于它能够显示更多更详细的有关TCP和连接状态的信息,而且比n ...

  10. SQL DISTINCT去掉重复的数据统计方法【转】

    SELECT指令让我们能够读取表格中一个或数个栏位的所有资料.这将把所有的资料都抓出,无论资料值有无重复.在资料处理中,我们会经常碰到需要找出表格内的不同资料值的情况.换句话说,我们需要知道这个表格/ ...