技术标签: JavaScript js es6 javascript
1.必须有函数嵌套函数;
2.内部函数必须使用到了外部函数的变量。
3.必须返回使用了外部变量的内部嵌套函数
通(hu)俗(bian)来(luan)讲(zao),为了便于理解,接下来我们做个代入。
下面这个人就是你,你有一个女朋友(任何你喜欢的女人都可以),你努力工作赚钱,然后你的女朋友花你的钱,女人花男人钱,这个就是“闭包”。
function myself() {
var money = 100;
function girlFriends() {
//不要在意后面的“s”
return console.log('花钱', money);
}
return girlFriends();
}
myself();//花钱 100
上面的myself
函数执行的时候,girlFriends
(女朋友)可以随便使用money
,虽然心疼,但是也还算合理对吧,用就用了。
可惜天不随人愿,函数的生命都是很短暂地,函数调用完成之后就没有了,内部的money
也会释放掉,人死了就什么都没有了,但是有女朋友的人会有一点微妙变化。
function myself() {
var money = 100;
function girlFriends() {
return console.log('花钱', money);
}
return girlFriends; //返回函数,而不是返回函数调用结果
}
myself()(); //增加一个(),保证返回的函数可以执行调用
//花钱 100
看看上面的代码,明明myself
已经执行结束了,返回了一个函数,调用这个函数,居然还能访问到money
,说明你死了,但是你的女朋友还是能花你的钱o(╥﹏╥)o
翻译一下上面的话,为什么你死了,你的女朋友还是能花你的钱。
这需要先讲讲钱是怎么挣的,你拼命工作,好不容易从老板(内存)那里挣了点儿钱。
但是这个老板很鸡贼,他给你钱后你必须时时刻刻盯着,如果没有人盯着这个钱,老板就会把钱拿走(垃圾回收)。
如果这个时候你有了女朋友(内部函数),并且返回了女朋友(内部函数),女朋友就可以帮你盯着钱,那么即便你死了,还是有人盯着钱,老板不能收走。所以只要有人在盯着这个钱,老板就不能收走。
接下来我们改造一下自己
function myself() {
var money = 100;
return function () {
return money++;}; //这里省略了一下girlFriend的名称,直接返回一个匿名函数
}
var earnMoney = myself();
earnMoney();
earnMoney();
earnMoney();
console.log('earnMoney', earnMoney());
//earnMoney 103
定义一个变量earnMoney
,把返回值赋值给这个变量,然后执行三次调用,结果输出了103。
因为我们的myself
函数只执行了一次,那么money
只初始化了一次,而且一直有函数在盯着存放money
的内存,所以没有被回收,然后里面内嵌的函数执行了三次,那么money++
运行了三次,所以返回103。也就是说现在我们已经从内存当中申请了一块区域用来存放“103”了,这块区域会一直存在,如何才能释放这块区域,只要让
earnMoney = null;
就可以了。
上面这段话翻译一下,找一个“房子”,用来存放我们返回的“女朋友”,然后呢女朋友可以帮我们盯着钱,这时候钱还是100,这个女朋友很会理财,每执行一次,她就会让我们的钱增加1块,执行一次就增加一块,虽然“我们自己”已经挂了,但是女朋友还在,她执行了三次,我们的钱增加了3块。而且存钱的这个地方,别人访问不到,只有女朋友能访问。
现在老板(内存)必须要把钱收回来了,那只能销毁“房子”来销毁女朋友,进而没有人盯着钱了,那么好不容易挣的钱就被收走了。
function myself() {
var money = 100;
return function () {
return money++;};
}
var earnMoney = myself();
var earnMoneyDouble = myself();
earnMoney();
earnMoney();
earnMoney();
earnMoneyDouble();
earnMoneyDouble();
earnMoneyDouble();
console.log('earnMoney', earnMoney()); //earnMoney 103
console.log('earnMoneyDouble', earnMoneyDouble()); //earnMoneyDouble 103
上面我们把返回函数赋值给了两个变量,分别执行两个变量,他们的计算互不影响,可以独立运行。这是因为计算的时候是在两块独立的内存上进行的。
接下来转换成我们熟悉的语言,现在找了两个房子,每一个房子都是调用myself(我们自己)之后得来的,说明我们很努力,挣了两份钱,分别放到两套房子里,这两份钱都让女朋友帮忙盯着,当女朋友去到第一个房子里执行理财的时候,第一个房子里面的钱就增加1块,这时第二套房子里的钱跟第一套。
所以“女人花男人的钱(闭包)“的好处就是,永远有钱花,而且钱只属于自己,不会被拿走,除非“房子”被铲掉。
接下来,该说一点男人们梦寐以求的事情了
答案就是,一碗水端平,每个都要疼,都得给钱花(为了让你们听懂,我才这样说的)
回到正题,请看代码
function myself() {
var manyGirlFriends = [];
for (var i = 0; i < 10; i++) {
manyGirlFriends[i] = function () {
return i;
};
}
return manyGirlFriends;
}
var girlsHouse = myself();
girlsHouse[5]();
console.log('girlsHouse[1]()', girlsHouse[1]()); // girlsHouse[1]() 10
console.log('girlsHouse[2]()', girlsHouse[2]()); // girlsHouse[2]() 10
console.log('girlsHouse[3]()', girlsHouse[3]()); // girlsHouse[3]() 10
console.log('girlsHouse[4]()', girlsHouse[4]()); // girlsHouse[4]() 10
console.log('girlsHouse[5]()', girlsHouse[5]()); // girlsHouse[5]() 10
上面代码里面,我们循环创建10个闭包,然后想让每个闭包返回当前的i,但是当myself
返回的时候,for
循环已经结束,所有闭包都会共享一个i
,那么结果就是全都会返回10。
接下来是大家期待的翻译时间
你有10个女朋友,你希望第一个女朋友分一块钱,第二个分两块钱,依次类推,但是这些女朋友不会听你的,她们是好姐妹,有钱一起花,每个人都能支配你赚来的钱。
还真有!立即执行函数
function myself() {
var manyGirlFriends = [];
for (var i = 0; i < 10; i++) {
(function (i) {
manyGirlFriends[i] = function () {
return i;
}
})(i);
}
return manyGirlFriends;
}
var girlsHouse = myself();
girlsHouse[5]();
console.log('girlsHouse[1]()', girlsHouse[1]()); //girlsHouse[1]() 1
console.log('girlsHouse[2]()', girlsHouse[2]()); //girlsHouse[2]() 2
console.log('girlsHouse[3]()', girlsHouse[3]()); //girlsHouse[3]() 3
console.log('girlsHouse[4]()', girlsHouse[4]()); //girlsHouse[4]() 4
或者还可以使用ES6的 let
,把for循环当中var i = 0
替换为 let i = 0
就可以了。
关于立即执行函数和let作用域问题,我们可以单独抽出来讨论一下,请关注我之后的博客
本片文章借鉴了《JavaScript权威指南》一书,还有很多技术界的前辈们,如果涉及侵权的地方,请联系我删除,另外画画和举例子是为了有直观的感受,并无恶意,请各位不要对号入座。还有本文是自己对于闭包和JS垃圾回收的一点浅显理解,有不准确的地方,请大佬多多指点,小弟拜上。
源文件下载地址:https://gitee.com/hwaust/WindGoes6/blob/master/Windgoes6/Data/CSV.cs简介网上有很多CSV的读写类,这些类要么设计不合理,要么功能简单,要么内容不完整。为了方便在C#在对CSV中的操作,笔者进行了重新编写。此CSV类具有以下特点:使用简单只需要几行代码即可定义、访问和进行相应的读写操作。功能完善包括CS...
HC-欢迎使用Markdown编辑器你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。新的改变我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:全新的界面设计 ,将会带来全新...
战争中你被俘了,敌人拷问你情报。你是这么想的:如果我把情报都告诉他们,他们就会认为我没有价值了,就会杀了我省粮食,但如果我死活不说,他们也会认为我没有价值而杀了我。怎样才能做到既让他们确信我知道情报,但又一丁点情报也不泄露呢?这的确是一个令人纠结的问题,但阿里巴巴想了一个好办法,当强盗向他拷问打开山洞石门的咒语时,他对强盗说:“你们离我一箭之地,用弓箭指着我,你们举起右手我就念咒语打开石门,举...
plt.rcParams[‘font.sans-serif’] = [‘Arial Unicode MS’] 能显示中文
常规->Java堆内存,右边条拉大,建议一半即可,如果需要可3/4,太大不知道会出现什么bug。点击应用,再点确定,然后重启MATLAB,再看是否已经调好大小。圈出来的预设(英文preference)测试程序是否没问题。
--user-data-dir 浏览器存储用户配置文件的目录。--profile-directory 选择要与启动的第一个浏览器关联的配置文件目录。user-data-dir 下可以有多个 profile-directory,每创建一个user就就会多一个profile-directory,其中默认的profile-directory位于“user-data-dir\Default”,之后每创建一个,对应的文件夹名称是:“user-data-dir\Profile 2”、“use...
今天为大家讲解的是关于JAVA从入门到精通之Javaweb开发总结的问题!如何开发浏览器?B/S比C/S好动态脚本语言:JSPASPPHP客户端技术:HTMLCSS(叠层样式表) flash客户端标本语言:JavaScript(Ajax里的) vbscript服务端技术:CGI(过时了) ASP(微软的,不太好用) PHP ASP.NETJSP(最好的)DOM技术(树形结构化文件,是一个接...
PMI-PMP远程模考三总题数:200 答题数:200 正题:168 错题:32 未答:0单选题(每题1分,共29道题)6、[单选]在某建设工程项目的地基开挖过程中,对于因气候原因造成的进度拖后风险,项目经理采取了风险接受的策略。由于在施工期间下特大雨,工程不得不停工3天。项目经理应该怎么做?During the foundation excavation of a construction project, the project manager adopts risk ...
C++标准库类型——迭代器基础介绍 迭代器提供对一个容器中的对象的访问方法,并且定义了容器中对象的范围。 迭代器就如同一个指针。事实上,C++的指针也是一种迭代器。但是,迭代器不仅仅是指针,因此你不能认为它们一定具有地址值。例如,一个数组索引,也可以认为是一种迭代器。 除了使用下标来访问 vector 对象的元素外,标准库还提供了另一种访问元素的方法:使用迭代(iterator)。
我们在很多时候都会用PPT文件来进行汇报或者总结,因为PPT文件展示起来非常方便,而且大家还可以根据需求来选择PPT的模板,这样就可以让我们的文件变得更加精美,而且也会给别人留下好印象,为自己加分。不过在进行文件传输和观看的时候,我们通常需要将PPT文件转换成PDF文件,这样就可以更简单的了解到文件的主要内容,更便于浏览一些,但是PPT怎么转换PDF呢?其实只需要几步操作,我们就可以将文件进行转换,今天小编就给大家介绍几种方法,一起来看看吧! PPT怎么转换PDF?方法一:小圆象PDF转换器小编使用过市面上
Vue3-Scroll 基于vue3.0构建的桌面端虚拟美化滚动条组件。一款基于vue3.x构建的pc端自定义模拟滚动条|vue3.0美化滚动条组件。支持监听DOM尺寸变化、是否原生滚动、是否自动隐藏滚动条、自定义尺寸/颜色及层级等功能。引入组件import { createApp } from 'vue'import App from './App.vue'import './index.css'// 引入滚动条组件v3scrollimport V3Scroll from
问:关于traceroute的原理有一点困惑,如果要得到单次测量的完整传输路径,那么必须每个分组每次往外发经过的路由(返回报文的话怎么走不影响)都是一样的才行吧?路由转发表什么的不是会更新吗? 答:不能。 原文链接:https://www.zhihu.com/question/51514289 ...