js的闭包

function a(){var n = 0;this.inc = function () {n++; console.log(n);};
}
var c = new a();
c.inc();    //控制台输出1
c.inc();    //控制台输出2

什么是闭包?这就是闭包!!有权访问另一个函数作用域内变量的函数都是闭包。当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。

这里 inc 函数访问了构造函数 a 里面的变量 n,所以形成了一个闭包。

function a(){var n = 0;function inc(){n++; console.log(n);}return inc;
}
var c = a();
c();    //控制台输出1
c();    //控制台输出2

执行过程:

var c=a();这里的a()返回的是函数inc,那这句等同于var c = inc;c()相当于inc();函数名只是一个标识(指向函数的指针),而()才是执行函数。

为什么要用闭包?

js的每个函数都是一个个小黑屋,它可以获取外界信息,但是外界却无法直接看到里面的内容。将变量 n 放进小黑屋里,除了 inc 函数之外,没有其他办法能接触到变量 n,而且在函数 a 外定义同名的变量 n 也是互不影响的,这就是所谓的增强“封装性”。而之所以要用 return 返回函数标识 inc,是因为在 a 函数外部无法直接调用 inc 函数,所以 return inc 与外部联系起来,第一个代码中的 this 也是将 inc 与外部联系起来而已。

常见的陷阱

function createFunctions(){var result = new Array();for (var i=0; i < 10; i++){result[i] = function(){return i;};}return result;
}
var funcs = createFunctions();
for (var i=0; i < funcs.length; i++){console.log(funcs[i]());
}

原本想要输出0~9,但是最后的结果是10个10,为什么呢?

这里的陷阱就是:函数带()才是执行函数! 单纯的一句 var f = function() { alert('Hi'); }; 是不会弹窗的,后面接一句 f(); 才会执行函数内部的代码。上面代码翻译一下就是:

var result = new Array(), i;
result[0] = function(){ return i; }; //没执行函数,函数内部不变,不能将函数内的i替换!
result[1] = function(){ return i; }; //没执行函数,函数内部不变,不能将函数内的i替换!
...
result[9] = function(){ return i; }; //没执行函数,函数内部不变,不能将函数内的i替换!
i = 10;
funcs = result;
result = null;console.log(i); // funcs[0]()就是执行 return i 语句,就是返回10
console.log(i); // funcs[1]()就是执行 return i 语句,就是返回10
...
console.log(i); // funcs[9]()就是执行 return i 语句,就是返回10

为什么只垃圾回收了 result,但却不收了 i 呢? 因为 i 还在被 function 引用着啊。这就是闭包的神奇之处,内部作用域依然存在,因此没有被回收。result依然持有对该作用域的引用,这个引用就叫做闭包!

更改方法:

function createFunctions(){var result = new Array();for (var i=0; i < 10; i++){result[i] = function(x){return function(){return x;}}(i);}return result;
}
var funcs = createFunctions();
for (var i=0; i < funcs.length; i++){console.log(funcs[i]());
}

没有直接把闭包赋值给数组,而是定义了一个匿名函数,并通过立即执行该匿名函数的结果赋值给数组,并带了for循环的参数i进去,让x能找到传入的参数值为0-10,这就解释了函数参数是按值传递的,所以会将变量i的当前值复制给参数x。而这个匿名函数内部又创建并返回了一个访问x的闭包。这样以来result数组中的每个函数都有自己x变量的一个副本,所以会符合我们的预期输出不同的值。

闭包的应用场景

闭包应用的两种情况:函数作为返回值,函数作为参数传递

1、函数作为返回值

function fn(){var max=10;return function bar(x){if (x>max){console.log(x);}};  
}
var f1=fn();
f1(15);

bar函数作为返回值,赋值给f1变量。执行f1(15)时,用到了fn作用域下的max变量的值。

2、函数作为参数被传递

var max = 10;
var fn=function(x){if(x>max){console.log(x);}
};
(function(f){var max = 100;f(15);
})(fn);

fn函数作为一个参数被传递进入另一个函数,赋值给f参数。执行f(15)时,max变量的取值是10,而不是100,因为要去创建这个函数的作用域取值,而不是“父作用域”。

当一个函数被调用完成之后,其执行上下文环境将被销毁,其中的变量也会被同时销毁。但有些情况下,函数调用完成之后,其执行上下文环境不会接着被销毁。这就是需要理解闭包的核心内容。如下:

第一步,代码执行前生成全局上下文环境,并在执行时对其中的变量进行赋值。此时全局上下文环境是活动状态。

 

第二步,执行第17行代码时,调用fn(),产生fn()执行上下文环境,压栈,并设置为活动状态。

第三步,执行完第17行,fn()调用完成。按理说应该销毁掉fn()的执行上下文环境,但是这里不能这么做。注意,重点来了:因为执行fn()时,返回的是一个函数。函数的特别之处在于可以创建一个独立的作用域。而正巧合的是,返回的这个函数体中,还有一个自由变量max要引用fn作用域下的fn()上下文环境中的max。因此,这个max不能被销毁,销毁了之后bar函数中的max就找不到值了。

因此,这里的fn()上下文环境不能被销毁,还依然存在与执行上下文栈中。——即,执行到第18行时,全局上下文环境将变为活动状态,但是fn()上下文环境依然会在执行上下文栈中。另外,执行完第18行,全局上下文环境中的max被赋值为100。如下图:

第四步,执行到第20行,执行f1(15),即执行bar(15),创建bar(15)上下文环境,并将其设置为活动状态。

执行bar(15)时,max是自由变量,需要向创建bar函数的作用域中查找,找到了max的值为10。这个过程在作用域链一节已经讲过。

这里的重点就在于,创建bar函数是在执行fn()时创建的。fn()早就执行结束了,但是fn()执行上下文环境还存在与栈中,因此bar(15)时,max可以查找到。如果fn()上下文环境销毁了,那么max就找不到了。

这里明显可以看到,使用闭包会增加内容开销!

第五步,执行完20行就是上下文环境的销毁过程。

总结

闭包就是一个函数引用另外一个函数的变量,因为变量被引用着所以不会被回收,因此可以用来封装一个私有变量。这是优点也是缺点,不必要的闭包只会徒增内存消耗!另外使用闭包也要注意变量的值是否符合你的要求,因为他就像一个静态私有变量一样。

转载于:https://www.cnblogs.com/lmjZone/p/9400300.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/368001.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Background-size完美兼容IE

CSS3 新增的 background-size 是一个很有用的属性&#xff0c;用于定义背景图片的尺寸&#xff0c;有了这个属性&#xff0c;你就可以任意指定背景图片的大小。其中最常用的值应该要数 cover 了&#xff0c;该值能让背景图片缩放至填满整个容器&#xff0c;即使是图片面积小于容…

python第五章上机实践报告_第五章实践报告 - osc_kk5bjg1i的个人空间 - OSCHINA - 中文开源技术交流社区...

1.实践问题&#xff1a;工作分配问题2.问题描述设有n件工作分配给n个人。将工作i分配给第j个人所需的费用为cij 。 设计一个算法&#xff0c;对于给定的工作费用&#xff0c;为每一个人都分配1 件不同的工作&#xff0c;并使总费用达到最小。输入格式:输入数据的第一行有1 个正…

android服务下载,android服务之bindService和unService中下载任务中的应用

通过bindService方法来调用服务final Down down data;viewHolder.videoActionBtn.setOnClickListener(new View.OnClickListener() {Overridepublic void onClick(View v) {if(bEditMode) {// 处于编辑模式&#xff0c;点击删除deleteDown(down);} else {// 处于播放模式&…

02CSS文本样式08

day08 CSS文本样式   font-family设置字体   font-size文字大小   font-size绝对单位|相对单位   cm mm pt pc xx-small   x-small medium large small xx-large x-large   相对单位&#xff1a;px em % larger smaller   color文字颜色   color:颜色|十六进制…

Illustrator、Indesign与Photoshop

Adobe illustrator是一种应用于出版、多媒体和在线图像的工业标准矢量插画的软件&#xff0c;作为一款非常好的图片处理工具&#xff0c;Adobe Illustrator广泛应用于印刷出版、海报书籍排版、专业插画、多媒体图像处理和互联网页面的制作等&#xff0c;也可以为线稿提供较高的…

判断sem信号量为零_将信号量递减为零的进程崩溃时,如何恢复信号量?

我有多个使用g 编译的应用程序&#xff0c;它们在Ubuntu中运行。我正在使用命名信号量来协调不同进程之间的关系。除非 出现以下情况&#xff0c; 否则所有方法都可以正常工作&#xff1a;如果其中一个进程调用sem_wait()或sem_timedwait()使信号量递减&#xff0c;然后在有机会…

在Spring中使用Future对象调用Async方法调用

下一个示例将演示Spring容器内部的异步方法调用。 为什么我们需要异步方法调用&#xff1f; 在某些情况下&#xff0c;我们并不真正知道是否需要重播或何时应返回结果。 传统方式在Java EE的异步处理世界中&#xff0c;使用队列/主题。 我们可以在Spring中进行相同的操作&#…

select里加链接

html select标签加链接的方法有很多&#xff0c;接下来为大家介绍下几个比较经典的&#xff0c;,感兴趣的朋友可以参考下哈&#xff0c;希望可以帮助到你 第一种 &#xff1a; <SCRIPT languagejavascript> <!-- // open the related site windows function mbar…

android ipc 多个客户端,Android IPC之AIDL进阶篇

前言在Android IPC之AIDL中我介绍了如何使用AIDL进行多进程通信&#xff0c;不过由于当时个人水平有限&#xff0c;仅仅介绍了最基础的部分&#xff0c;所以本篇博客主要是在Android IPC之AIDL的基础上深入介绍下AIDL的进阶的几点理解以及用法。AIDL接口中的in out inout的含义…

mysql数据库(3)-查询

数据库设计规范 58到家数据库30条军规解读查询 创建数据库、数据表 -- 创建数据库 create database python_test_1 charsetutf8; -- 使用数据库 use python_test_1; -- students表 create table students( id int unsigned primary key auto_increment not null, name varchar(…

Spring Data Solr教程:向所有存储库添加自定义方法

如果我们在现实生活中的软件项目中使用Spring Data Solr&#xff0c;很可能我们迟早会遇到一个要求&#xff0c;该要求指出我们的应用程序必须能够与本地Solr服务器和SolrCloud进行通信 。 目前&#xff0c;满足此要求意味着我们必须向所有Spring Data Solr存储库添加自定义方法…

远程管理口怎么看地址_红烧羊肉怎么样做才能滋味浓郁,咸甜适口,且回味有奶香?看这里...

原汁原味红烧羊肉此菜在制作上不同于其他红烧羊肉时要放入香料去膻&#xff0c;但在选料上很讲究&#xff0c;也就是说食材的好坏决定菜的好坏。选用一年生的崇明母山羊制作&#xff0c;膻味很小&#xff0c;肉质软嫩细腻&#xff0c;且带有一股淡淡奶香&#xff0c;因此不必放…

css段落文字(中英文混杂)实现两端对齐

案例如下&#xff1a; 混合使用汉字和英文的段落默认如下&#xff1a; 两边是不对齐的(一般情况下&#xff0c;我们对这种情况不做处理&#xff0c;除非需求或者设计非要我们实现两端对齐)。 对齐之后如下&#xff1a; 实现思路 一般的两端对齐是使用text-align:justify&…

44集合:蒜头军学英语

转载于:https://www.cnblogs.com/passion-sky/p/8424769.html

android病毒下载地址,LINE病毒查杀

LINE病毒查杀是免费通话、免费传讯「LINE」的周边应用程序之一。它能保护智能手机上个人信息的安全&#xff0c;使其免于病毒或恶意程序的侵害。您只要执行几个简单的步骤就能确认手机状态或完成病毒扫描。LINE病毒查杀界面LINE病毒查杀软件功能1、智能手机上的病毒将无所遁形!…

Golang系列:打印命令行参数

记得最早在学校机房学习 Java 时&#xff0c;照着书上的例子&#xff0c;写一个最简单 main 方法&#xff0c;当程序运行并在屏幕上打印出 hello world 时&#xff0c;内心竟有种莫名的激动&#xff0c;相信很多人都有这种经历吧。 不管学什么编程语言&#xff0c;都先从命令行…

Javascript 两种 function 定义的区别

大家都知道Javascript 有两个种定义Function的方法非常常用。例如 function a(){ alert ( "a" )} var a function (){ alert ( "a" )} 虽然两个种方式定义出来的 function 调用的时候结果一样&#xff0c;但是中间还是有区别的。举个简单的…

android app的签名,Android APP的签名

Android APP的签名Android项目以它的包名作为唯一的标识&#xff0c;如果在同一部手机上安装两个包名相同的APP&#xff0c;后者就会覆盖前面安装的应用。为了避免Android APP被随意覆盖&#xff0c;Android要求对APP进行签名。下面介绍对APP进行签名的步骤1、选择builder菜单下…

5.6.50 mysql 用什么驱动_日均5亿查询量的京东订单中心,为什么舍弃MySQL用ES?

京东到家订单中心系统业务中&#xff0c;无论是外部商家的订单生产&#xff0c;或是内部上下游系统的依赖&#xff0c;订单查询的调用量都非常大&#xff0c;造成了订单数据读多写少的情况。我们把订单数据存储在MySQL中&#xff0c;但显然只通过DB来支撑大量的查询是不可取的。…

java常用知识

1、transient 修饰的关键字不参与序列号转载于:https://www.cnblogs.com/ng1991/p/8425694.html