JavaScript简餐——闭包
文章目录
- 前言
- 一、闭包概念
- 二、闭包的副作用
- 三、闭包的用途
- 1.在函数外部读取函数的内部变量
- 2.利用外部函数变量缓存值
- 四、总结
前言
写本《JavaScript简餐》系列文章的目的是记录在阅读学习《JavaScript高级程序设计(第4版)》一书时出现的各个知识点。虽是对读书的笔记和总结,但是希望它轻量、简洁、犀利,不会引起阅读疲劳,可以在碎片化时间和闲暇之余轻巧地沐浴一下知识点。每篇文章只针对一个小部分进行讲解式的梳理,来达到个人复习总结和分享知识的目的。
一、闭包概念
闭包:指的是那些引用了另一个函数作用域中变量的函数。通常是在嵌套函数中实现的。例如:
function Person() {let age = 29;return function sayAge() {console.log(age);};
}
在这里,内部函数sayAge函数引用了外部函数Perosn的变量age,即应用了另一个函数作用域中的变量。所以,sayAge函数就是一个闭包。
二、闭包的副作用
一般函数在执行完毕后,局部活动对象会被销毁,内存中就只剩下全局作用域。(在这里要说明一下,函数执行上下文中会有一个包含其中变量的对象,这个对象中包含函数的arguments、函数局部变量以及函数中定义的对象,我们称这个对象为变量对象。而在函数执行时,这个变量对象就称为活动对象。)但是当你定义一个闭包后,由于闭包会保留外部函数的作用域,导致外部函数的活动对象不能在它执行完毕后销毁,此时闭包的作用域链中仍然有对外部函数活动对象的引用。来看一个例子:
function Person() {let age = 29;addAge = function () { // addAge没有用var、let或者const声明,所以相当于定义在全局的函数++age;};return function sayAge() {console.log(age);};
}let result = Person();result(); // 29
addAge();
result(); // 30
在这里,我们先运行result()(要知道我们的这个result函数等于sayAge函数),得到了age的值29,之后我们借助addAge()将age的值加1,再次运行result()时我们发现age的值变成30了。这足以证明,Person函数的局部变量age一直存在于内存中,并没有在调用后被销毁。这就会导致垃圾回收程序无法回收这个变量,从而引发内存泄漏。
对此,我们可以通过调用result()后将其设置为null来解除对外部函数的引用来释放内存,如下:
function Person() {let age = 29;addAge = function () { // addAge没有用var、let或者const声明,所以相当于定义在全局的函数++age;};return function sayAge() {console.log(age);};
}let result = Person();result(); // 29result = null; // 解除对函数的引用,这样就可以释放内存
闭包会保留包含它们的函数的作用域,所以比其他函数更占用内存。过度使用闭包可能导致内存过度占用,因此建议仅在十分必要时使用。
三、闭包的用途
1.在函数外部读取函数的内部变量
众所周知,在函数外部时不可以调用函数内部定义的变量的。但是通过闭包我们可以访问到函数的内部变量。具体原理是在函数中将闭包作为返回值返回,之后在函数外部再次调用这个闭包即可。例如:
function Person() {let age = 29;return function sayAge() {console.log(age);};
}let result = Person(); // 29
//当然也可以这样调用:
Person()();
2.利用外部函数变量缓存值
以防抖函数为例:
function debounce(fn, wait = 1000) {let timer;return function () {if (timer) {clearTimeout(timer);}timer = setTimeout(() => {fn.apply(this, arguments);}, wait);};
}
这里利用闭包,让外部函数定义的timer一直存在于内存中不被销毁,当防抖函数再次执行时就可以直接从内存中读取timer来判断是否还存在,从而来进行防抖限制。
四、总结
以上就是今天要讲的内容,今天介绍了一下闭包的概念、闭包的副作用以及它的作用。下一篇,我们来讲一下ES6新增的尾调用优化。撒花~
JavaScript简餐——闭包相关推荐
- JavaScript简餐——函数尾调用优化
文章目录 前言 一.认识尾调用优化 二.尾调用优化的条件 三.尾调用优化实例 总结 前言 写本<JavaScript简餐>系列文章的目的是记录在阅读学习<JavaScript高级程序 ...
- JavaScript简餐——那些函数属性与方法(call、apply和bind)
文章目录 前言 一.arguments对象的callee属性 二.函数的caller属性 三.new.target 四.call.apply和bind方法 1.call方法 2.apply方法 3.b ...
- JavaScript简餐——关于盗用构造函数
文章目录 前言 一.什么是盗用构造函数? 二.使用实例 三.参数传递 四.盗用构造函数的问题所在 五.总结 前言 写本<JavaScript简餐>系列文章的目的是记录在阅读学习<Ja ...
- JavaScript简餐——初见Promise
文章目录 前言 一.Promise基础 1.Promise状态机 2.通过执行函数 控制Promise的状态 3.Promise.resolve() 4.Promise.reject() 5.同步/异 ...
- JavaScript简餐——初识函数
文章目录 前言 初始函数 1.函数声明 2.函数表达式 3.箭头函数(ES6新特性) 4.使用Function构造函数 总结 前言 写本<JavaScript简餐>系列文章的目的是记录在阅 ...
- JavaScript简餐——类构造函数
文章目录 前言 一.类的构造函数及其实例化 二.把类当成特殊函数 三.总结 前言 写本<JavaScript简餐>系列文章的目的是记录在阅读学习<JavaScript高级程序设计(第 ...
- JavaScript简餐——寄生组合继承
文章目录 前言 一.什么是寄生组合继承? 二.寄生组合继承的基本模式 三.总结 前言 写本<JavaScript简餐>系列文章的目的是记录在阅读学习<JavaScript高级程序设计 ...
- JavaScript简餐——原型式继承
文章目录 前言 一.什么是原型式继承? 二.ECMAScript5中的原型式继承 三.总结 前言 写本<JavaScript简餐>系列文章的目的是记录在阅读学习<JavaScript ...
- JavaScript简餐——细看函数的参数
文章目录 前言 一.理解参数 二.箭头函数中的参数 三.总结 前言 写本<JavaScript简餐>系列文章的目的是记录在阅读学习<JavaScript高级程序设计(第4版)> ...
最新文章
- oracle 条件动态视图,oracle最重要的9个动态性能视图
- 输入n个字符串字典序排序_FSST - 快速字符串压缩算法
- android listview显示数据库内容
- effective C++ 条款 47:使用traits classes表现类型信息
- 【行业报告】中国金融科技2017专题研究报告——易观智库
- Effective Java之通过私有构造器强化不可实例化能力(四)
- 【CodeForces - 155C】Hometask (字符串,思维,贪心,熟悉句式)(总结)
- php react-native,React-Native+Mobx实现商城APP
- 定时器加状态转移图方式实现DS18B20
- matlab中二阶偏导数,matlab中二元函数的一阶和二阶偏导数
- 【VUE】vue程序设计----模仿网易严选
- Git Extension 合并分支
- 高价值的聚合支付源码修复版+带后台/语音播报
- 使用DISM修复系统
- 修真院java_【修真院JAVA小課堂】JMeter的簡單介紹
- openeuler 21.3 : 使用LVM管理硬盘
- 抖音直播用什么手机效果最好 抖音直播手机哪款好2023
- 2019年2月18日,异常作业
- 在Mybaties中进行数据查询时,表中字段名和属相名没有对应起来,查询的结果总null,一下是解决方法
- 【分享】在小米实习到SP+转正