JavaScript闭包,有女朋友的进来_不见浅诗~的博客-程序员秘密

技术标签: JavaScript  js  es6  javascript  

什么是闭包

1.必须有函数嵌套函数;
2.内部函数必须使用到了外部函数的变量。
3.必须返回使用了外部变量的内部嵌套函数


怎么理解闭包

通(hu)俗(bian)来(luan)讲(zao),为了便于理解,接下来我们做个代入。

  • 你 = 外层函数myself
  • 女朋友 = 内层函数girlFriends
  • 钱 = 外层变量money
  • 老板 = 内存

下面这个人就是你,你有一个女朋友(任何你喜欢的女人都可以),你努力工作赚钱,然后你的女朋友花你的钱,女人花男人钱,这个就是“闭包”。
在这里插入图片描述

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垃圾回收的一点浅显理解,有不准确的地方,请大佬多多指点,小弟拜上。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/EcbJS/article/details/104924009

智能推荐

一个完整使用C#编写的CSV读写类_c# 写csv文件 追加写入_郝伟博士的博客-程序员秘密

源文件下载地址:https://gitee.com/hwaust/WindGoes6/blob/master/Windgoes6/Data/CSV.cs简介网上有很多CSV的读写类,这些类要么设计不合理,要么功能简单,要么内容不完整。为了方便在C#在对CSV中的操作,笔者进行了重新编写。此CSV类具有以下特点:使用简单只需要几行代码即可定义、访问和进行相应的读写操作。功能完善包括CS...

HC-05蓝牙模块连接蓝牙打印机步骤_at+inq_liuwei36120516的博客-程序员秘密

HC-欢迎使用Markdown编辑器你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。新的改变我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:全新的界面设计 ,将会带来全新...

阿里巴巴的零知识证明_跨链技术践行者的博客-程序员秘密

战争中你被俘了,敌人拷问你情报。你是这么想的:如果我把情报都告诉他们,他们就会认为我没有价值了,就会杀了我省粮食,但如果我死活不说,他们也会认为我没有价值而杀了我。怎样才能做到既让他们确信我知道情报,但又一丁点情报也不泄露呢?这的确是一个令人纠结的问题,但阿里巴巴想了一个好办法,当强盗向他拷问打开山洞石门的咒语时,他对强盗说:“你们离我一箭之地,用弓箭指着我,你们举起右手我就念咒语打开石门,举...

mac 下 matplotlib 中文字体问题解决_搬运代码打工人的博客-程序员秘密

plt.rcParams[‘font.sans-serif’] = [‘Arial Unicode MS’] 能显示中文

matlab报错java.lang.OutOfMemoryError_matlab显示java错误_战斗到永恒的博客-程序员秘密

常规->Java堆内存,右边条拉大,建议一半即可,如果需要可3/4,太大不知道会出现什么bug。点击应用,再点确定,然后重启MATLAB,再看是否已经调好大小。圈出来的预设(英文preference)测试程序是否没问题。

chrome 启动参数--user-data-dir 和 --profile-directory的关系_bigcarp的博客-程序员秘密

--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 web开发入门到精通_JAVA从入门到精通之Javaweb开发总结_weixin_39999209的博客-程序员秘密

今天为大家讲解的是关于JAVA从入门到精通之Javaweb开发总结的问题!如何开发浏览器?B/S比C/S好动态脚本语言:JSPASPPHP客户端技术:HTMLCSS(叠层样式表) flash客户端标本语言:JavaScript(Ajax里的) vbscript服务端技术:CGI(过时了) ASP(微软的,不太好用) PHP ASP.NETJSP(最好的)DOM技术(树形结构化文件,是一个接...

PMI-PMP远程模考三 错题:32_你是建设一个生猪养殖场项目的经理_微亮的博客-程序员秘密

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++标准库类型——迭代器_bear_n的博客-程序员秘密

C++标准库类型——迭代器基础介绍​ 迭代器提供对一个容器中的对象的访问方法,并且定义了容器中对象的范围。​ 迭代器就如同一个指针。事实上,C++的指针也是一种迭代器。但是,迭代器不仅仅是指针,因此你不能认为它们一定具有地址值。例如,一个数组索引,也可以认为是一种迭代器。​ 除了使用下标来访问 vector 对象的元素外,标准库还提供了另一种访问元素的方法:使用迭代(iterator)。

PPT怎么转换PDF?来试试这几种方法_xiaoyuanxiangPDF的博客-程序员秘密

我们在很多时候都会用PPT文件来进行汇报或者总结,因为PPT文件展示起来非常方便,而且大家还可以根据需求来选择PPT的模板,这样就可以让我们的文件变得更加精美,而且也会给别人留下好印象,为自己加分。不过在进行文件传输和观看的时候,我们通常需要将PPT文件转换成PDF文件,这样就可以更简单的了解到文件的主要内容,更便于浏览一些,但是PPT怎么转换PDF呢?其实只需要几步操作,我们就可以将文件进行转换,今天小编就给大家介绍几种方法,一起来看看吧! PPT怎么转换PDF?方法一:小圆象PDF转换器小编使用过市面上

Vue3.0虚拟滚动条|vue3自定义美化滚动条V3Scroll_vue3 scroll_xiaoyan_2018的博客-程序员秘密

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/tracert能保证每个分组都是走一样路由路径吗?_tracert如何保证不会多路由_啊大1号的博客-程序员秘密

问:关于traceroute的原理有一点困惑,如果要得到单次测量的完整传输路径,那么必须每个分组每次往外发经过的路由(返回报文的话怎么走不影响)都是一样的才行吧?路由转发表什么的不是会更新吗? 答:不能。 原文链接:https://www.zhihu.com/question/51514289 ...

推荐文章

热门文章

相关标签