闭包是一个老生常谈的问题,简单概括下闭包的形成的两个条件:

    1、定义在函数内部

    2、函数内部引用父层作用域变量

  举一个最简单的例子:

 function test() {
var a = 1; function hello() {
console.log(a);
}
return hello;
} var world = test();
world(); //

  以上代码会在控制台输出“1”。这是什么为什么呢?函数内部变量在调用结束后一般都会销毁,以上代码在test方法调用结束后并没有被销毁,这是由于js语言本身垃圾回收导致的。众所周知,js的垃圾回收机制是引用计数,当变量在其他作用域被引用时,该变量就不会被销毁,会一直存在内存中,这样就形成了闭包。在调试代码的过程中也可以在开发者工具中看出,如下图所示:

  以上可以看出,当前环境存在一个作用域,Closure(text),包含引用父层的作用域的变量a。

  js函数参数传递分为数值传递和引用传递,数值传递针对参数的类型为number/string/boolean....;引用传递针对参数类型为object/array....;所以在针对引用父层参数时,要注意父层参数类型,如果为数值传递,父层参数改变并不会影响闭包执行结果,但是如果参数是引用传递,要注意闭包里的参数引用的是父层变量地址,变量发生改变,闭包的执行结果会随之改变,以下代码可以看出:

 var obj = {
num: {
b: 1
}
} function test(obj) {
var a = obj.num; function hello() {
console.log(a.b);
}
return hello;
} var world = test(obj);
world(); //
obj.num.b = 2;
test(obj);
world(); //

  执行结果:

  再说一个经典的面试题:

 for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 0);
}

  想必大家都知道这段代码的执行结果是5个1,但是有没有想过为什么会是5个1呢?

  setTimeout函数比较特殊,其执行优先级要低于for循环,循环结束后才会被执行,其匿名函数类似于一个回调,匿名函数都引用了变量i,var声明的变量又不存在块级作用域,最终循环结束后,i值变为5,在回调执行过程中,会向父层作用域执行RHS查询(就是查找变量i的值是否存在),i的值目前存在于全局变量window中,但是已变成了5,所以会输出5个5。

  如何让其一次性输入0,1,2,3,4呢?

  其实问题的本源在于js语言中本来不存在块级作用域,for循环声明的变量i最终是在全局变量中,每次循环都会更新一次全局变量中的值,所以实现块级作用域有两种常用的方法,一是借助es6的声明变量的方法let,第二个是利用闭包;

  闭包的代码如下:

 for (var i = 0; i < 5; i++) {
setTimeout((function(i) {
console.log(i);
})(i), 0)
}

  正如文章刚开始所述,setTimeout回调中引用的是父层函数中变量i的值,在每次声明的时候,都会将当前的i值传递给闭包,其有自己单独的作用域,不再是引用同一变量,具体作用域情况如下所示:

  以上仅为个人看法,如有错误,烦请指出,谢谢!

浅析js闭包的更多相关文章

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

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

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

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

  3. 浅析JS中的模块规范(CommonJS,AMD,CMD)////////////////////////zzzzzz

    浅析JS中的模块规范(CommonJS,AMD,CMD)   如果你听过js模块化这个东西,那么你就应该听过或CommonJS或AMD甚至是CMD这些规范咯,我也听过,但之前也真的是听听而已.     ...

  4. Js闭包常见三种用法

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

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

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

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

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

  7. js闭包理解

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

  8. js闭包理解实例小结

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

  9. Js闭包的用途

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

随机推荐

  1. Confluence 6 如何配置快速导航的同时查找数量

    进入后台后查看快速导航的启用和可以同时查找的数量. 然后进行通过单击右上角的编辑(Edit)按钮进行编辑. 对配置进行配置,启用快速查询和可以同时使用的最大查询数量. https://www.cwik ...

  2. ajax补充--------FormData等...

    一.回顾上节知识点 1.什么是json字符串? 轻量级的数据交换格式 2.定时器:关于setTimeout setTimeout(foo,3000)  # 3000表示3秒,foo表示一个函数,3秒后 ...

  3. Python基础之封装

    一.什么是封装 在程序设计中,封装(Encapsulation)是对具体对象的一种抽象,即将某些部分隐藏起来,在程序外部看不到,其 含义是其他程序无法调用. 要了解封装,离不开“私有化”,就是将类或者 ...

  4. Allegro PCB Design GXL (legacy) 设置十字大光标

    Allegro PCB Design GXL (legacy) version 16.6-2015 1.菜单:Setup > User Preferences... 2.User Prefere ...

  5. 论文阅读笔记二-ImageNet Classification with Deep Convolutional Neural Networks

    分类的数据大小:1.2million 张,包括1000个类别. 网络结构:60million个参数,650,000个神经元.网络由5层卷积层,其中由最大值池化层和三个1000输出的(与图片的类别数相同 ...

  6. 20165206 实验一 Java开发环境的熟悉

    20165206 实验一 Java开发环境的熟悉 一.实验内容及步骤 实验一 Java开发环境的熟悉-1 建立有自己学号的实验目录. 通过vim Hello.java编辑代码. 编译.运行Hello. ...

  7. 去掉A标签的点击选中边框

    非IE a:focus { outline:none; }

  8. js中的原型

    一:原型属性 函数本身也是一个包含了方法和属性的对象. 定义一个函数foo(),访问其他对象一样访问该函数的属性: function foo(a, b) { return a * b; } foo.l ...

  9. python之 MySQLdb 实践 爬一爬号码

    0.目录 2.构建URL3.新建数据库4.新建汇总表5.定义连接数据库函数:connect_db(db=None, cursorclass=DictCursor)6.汇总表填充必要数据7.新建各省份子 ...

  10. NEST 中的日期数学表达式

    Date math expressions Version: 5.x 英文原文地址:Date math expressions query/filter 中涉及到日期类型时(如:timeout 参数) ...