浅析js闭包
闭包是一个老生常谈的问题,简单概括下闭包的形成的两个条件:
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闭包的更多相关文章
- js闭包的作用域以及闭包案列的介绍:
转载▼ 标签: it js闭包的作用域以及闭包案列的介绍: 首先我们根据前面的介绍来分析js闭包有什么作用,他会给我们编程带来什么好处? 闭包是为了更方便我们在处理js函数的时候会遇到以下的几 ...
- 大部分人都会做错的经典JS闭包面试题
由工作中演变而来的面试题 这是一个我工作当中的遇到的一个问题,似乎很有趣,就当做了一道题去面试,发现几乎没人能全部答对并说出原因,遂拿出来聊一聊吧. 先看题目代码: function fun(n,o) ...
- 浅析JS中的模块规范(CommonJS,AMD,CMD)////////////////////////zzzzzz
浅析JS中的模块规范(CommonJS,AMD,CMD) 如果你听过js模块化这个东西,那么你就应该听过或CommonJS或AMD甚至是CMD这些规范咯,我也听过,但之前也真的是听听而已. ...
- 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 我们来看看闭包的用途. ...
随机推荐
- nginx实战(三)之静态资源web服务(跨站访问)
语法 虽然说浏览器禁止跨站访问以防(CSRF),但出于一些原因还是要进行跨站访问,服务端通过设置头信息Access-Control-Allow-Orign:xxxx,客户端收到后就会允许跨站访问了 实 ...
- 对Swoole、Workerman和php自带的socket的理解
为什么php自带的socket不怎么听说,基本都是用swoole,workerman去实现? 1.PHP的socket扩展是一套socket api,仅此而已. swoole,用C实现,它的socke ...
- 《剑指offer》 二叉树的镜像
本题来自<剑指offer>二叉树的镜像 题目: 操作给定的二叉树,将其变换为源二叉树的镜像. 二叉树的镜像定义:源二叉树 8 / \ 6 10 / \ / \ 5 7 9 11 镜像二叉树 ...
- windows 下命令行启动停止mysql
MySQL比较好玩一点就是它可以用多种方式启动,当然它也可以用多种方式关闭.下面我就mysql的几种启动方式简单的谈一谈,希望可以给大家提供一些参考. 第一种,用mysqld-nt来启动. 在没有进行 ...
- 步步为营103-ZTree 二级联动
1:添加引用 <%--流程类别多选--引用js和css文件--开始--%> <link rel="stylesheet" href="../css/zT ...
- 该问题是需要导包!!!需要pom中添加依赖The absolute uri: http://java.sun.com/jsp/jstl/core cannot be resolved in either web.xml or the jar files deployed with this application
<!-- https://mvnrepository.com/artifact/org.apache.taglibs/taglibs-standard-impl --><depend ...
- k3 Bos开发百问百答
K/3 BOS开发百问百答 (版本:V1.1) K3产品市场部 目录 一.基础资料篇__ 1 [摘要]bos基础资料的显示问题_ 1 [摘要]单 ...
- android开发默认图标怎么换?
首先要在资源文件放入想换的图标图片拖到drawable-XX文件夹下, 然后打开AndroidManifest.xml这个配置清单文件找 到application标签里的这句android:icon= ...
- Task任务的屏障机制
Barrier 是 .Net 提供的一直并发的机制,它允许多个任务同步他们不同阶段的并发工作. 这里的关键点是[多个任务]和[不同阶段]. 假设有4个相同的任务(Task),每个任务都有4个阶段(Ph ...
- Zabbix监控——Zabbix自定义用户参数制作监控项
https://blog.51cto.com/183530300/2087774 https://www.cnblogs.com/richardzgt/articles/7889404.html