声明

本系列文章内容全部梳理自以下几个来源:

作为一个前端小白,入门跟着这几个来源学习,感谢作者的分享,在其基础上,通过自己的理解,梳理出的知识点,或许有遗漏,或许有些理解是错误的,如有发现,欢迎指点下。

PS:梳理的内容以《JavaScript权威指南》这本书中的内容为主,因此接下去跟 JavaScript 语法相关的系列文章基本只介绍 ES5 标准规范的内容、ES6 等这系列梳理完再单独来讲讲。

正文-闭包

在作用域链那篇中,稍微留了个闭包的念想,那么这篇就来讲讲什么是闭包。

概念

这个闭包的概念蛮不好理解的,我在阮一峰的某篇文章中看过大概这么句话,闭包是对英文单词的直译,在中文里没有与之对应的句子解释,因此很难理解闭包究竟指的是什么。

看过很多解释,有说闭包就是函数;也有说闭包就是代码块;还有说函数内的函数就称闭包;还有说当函数返回内部某个函数时,返回的这个函数叫闭包,也有说闭包就是能够读取其他函数内部数据(变量/函数)的函数。

MDN 网站里不同文章里出现过多种解释:

  1. 一个闭包是一个可以自己拥有独立的环境与变量的表达式(通常是函数)
  2. 闭包是函数和声明该函数的词法环境的组合,这个环境包含了这个闭包创建时所能访问的所有局部变量

另外,在某篇文章中,看过这么段话:

2009年发布了ECMAScript-262-5th第五版,不同的是取消了变量对象和活动对象等概念,引入了词法环境(Lexical Environments)、环境记录(EnviromentRecord)等新的概念

所以如果对词法环境这个词不理解的,可以将其理解成执行上下文,或者作用域链。在开头声明给的第四个链接中,是有几篇很早很早之前大佬们翻译的国外的文章,里面对闭包的解释刚好和 MDN 的解释也很类似:

闭包是代码块和创建该代码块的上下文中数据的结合

如果这个代码块是函数,那么利用作用域链那篇中介绍的相关原理,从本质上看闭包:

函数代码,和函数的内部属性 [[Scope]] 两者的结合可称为闭包。 :

对于这么多文章中对闭包的这么多种解释,先不做评价,先来想想,为什么会有闭包,理清了后,你会发现,其实理解闭包没那么难。

闭包意义

先看个例子:

var num = 0;
function a() {
var num = 1;
function b() {
console.log(num);
}
return b;
}
var c = a();
c();

调用 c() 输出的是 1,这点在作用域链那节已经讲解过了,这里再稍微说下:

调用 c(),会为 c 函数创建一个函数执行上下文,其中作用域链为:

c函数EC.VO –> a函数EC.VO -> 全局EC.VO

VO 是变量对象,表示存储着当前上下文中所有变量的对象,所以如果以 VO 的实际对象表示作用域链:

c函数{} –> a函数{num:1} -> 全局{num:0}

(忽略 VO 中其他与此例无关变量)

所以,函数 c 内的代码输出 num 时,到作用域链上寻找时,发现最后使用的是 a 函数内部的 num 变量,最终输出 1。

但当时也提了个疑问,当代码执行到 c() 时,a 函数已经执行结束,那么 a 函数的 EC 已经从执行环境栈 ECS 中被移出了,c 函数的 EC 里的作用域链为何还会有 a函数EC.VO 存在?

这就是闭包的典型场景了,闭包的意义之一就是解决这种场景。

通过作用域链一篇后,我们知道,函数内的变量依赖于函数执行上下文 EC,一般来说,当调用函数时,创建函数执行上下文 EC,并入栈 ECS,当函数执行结束时,就将 EC 从 ECS 中移出,并释放内存空间。

通常函数的行为的确是这样,但当函数如果有返回值时,情况就不一样了。虽然函数执行结束后它的 EC 确实被移出 ECS,但并没有被回收,JavaScript 解释器的垃圾回收机制也有引用计数的处理。

既然内存没被回收,那么 EC 就还存在,那么当调用 c() 时,虽然 C 的函数执行上下文是新创建的,上下文的作用域链也是新创建的,但作用域链的取值是当前执行上下文的 VO 拼接上函数对象的内部属性 [[Scope]]。

这个函数对象的内部属性 [[Scope]] 存储的就是这个函数的外层函数的执行上下文里的作用域链,它的值并不是新创建的,一直保存着外层函数调用时生成的外层函数上下文中的作用域链,通过它可以访问到外层函数变量。

再谈闭包概念

所以,实际上,网络上这么多文章里对闭包的各种解释,其实都没错。如果对作用域链的原理理解清楚后,你会发现,其实函数就是闭包,因为由于作用域的机制,让函数内部也持有创建函数的上下文的数据集合,所以函数符合闭包的特性。

只是在大部分场景下,函数执行结束,函数的 EC 就可以被回收,那么这种场景闭包并没有什么实际应用意义。

除了函数,如果你可以让某部分代码块持有创建它的上下文的数据集合,那么这也可以称为闭包。

常见的一种就是在函数内返回一个对象,对象的某些属性使用了对象外层的数据,如:

var model = (function () {
var num = 1;
return {
num:num
}
}());
model.num;

此时,也可以称返回的这个对象是闭包。

对于闭包,我对它的理解,更倾向于,闭包并不是一种机制,也不是一种具体的事物(如执行上下文),反而,闭包是对原本存在的事物满足某种场景下的一种称呼。

也就是说,闭包,它其实是在原有机制,原有事物上的另一种称呼。所以,网上也才有人会说,闭包是函数、闭包是内嵌的函数等等说法。其实,也不是说这是错的,他们有的是从闭包特性角度解释,有的是从闭包现象。

只是,这原本就存在的事物,你本可以就用它原本的称呼,既然想要用闭包来称呼它,那么自然是这个时候,称呼它为闭包有区别于原本事物的实际意义,所以也才有人会说当函数返回内部函数时,称为闭包,因为这种时候,返回的这个函数就是用到闭包的特性来解决某些问题,所以称这种现象为闭包当然就有实际应用场景意义了。

所以,我对闭包的理解,它并不是某个固定不变的东西,也不是某个具体的事物,只要符合闭包特性的原有事物,你都可以称它为闭包。所以,对于网上那些对闭包的解释,我的建议是,主谓互换一下,不要说闭包是函数,闭包是内嵌的函数等等,我们可以说,函数是闭包,内嵌的函数也是闭包。只要符合闭包特性的我们都可以称它为闭包,当然如果还有闭包的实际应用意义,那么称它为闭包更可以被人接受。

闭包的应用

作为外部和函数内部变量通信的桥梁

var model = (function () {
var num = 1;
function a() {
console.log(num);
}
return {
num:num
}
}());
model.num;

外部是访问不了函数内部的信息,而闭包是指代码块持有创建它的上下文的数据集合。那么,如果在函数内部创建一个闭包,将这个闭包返回给外部,外部是否就可以通过这个闭包作为桥梁来间接与函数内部通信了。

封装

var Counter = (function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
}
})();

还是同样的原因,外部是访问不了函数内部的信息,而闭包是指代码块持有创建它的上下文的数据集合。

那么,是否就可以借助闭包的特性,将一些实现封装在函数内部,通过闭包给外部提供有限的接口使用。

但要注意,函数本来执行结束,它的 EC 从 ECS 栈内移出时,通常就可被回收了,但如果用到了闭包的特性,导致外部持有着函数内部某个引用,此时函数的 EC 就不会被回收,那么就会占用着内存,使用不当,还会有可能造成内存泄漏。


大家好,我是 dasu,欢迎关注我的公众号(dasuAndroidTv),公众号中有我的联系方式,欢迎有事没事来唠嗑一下,如果你觉得本篇内容有帮助到你,可以转载但记得要关注,要标明原文哦,谢谢支持~

前端入门19-JavaScript进阶之闭包的更多相关文章

  1. 前端面试之JavaScript中的闭包!

    前端面试之JavaScript中的闭包! 闭包 闭包( closure )指有权访问另一个函数作用域中变量的函数. ----- JavaScript 高级程序设计 闭包其实可以理解为是一个函数 简单理 ...

  2. 前端基础之JavaScript进阶

    一.流程控制 if - else var a = 10; if (a >5){ console.log("yes"); }else { console.log("n ...

  3. 【进阶2-3期】JavaScript深入之闭包面试题解

    这是我在公众号(高级前端进阶)看到的文章,现在做笔记 https://github.com/yygmind/blog/issues/19 作用域指的是一个变量和函数的作用范围,JS中函数内声明的所有变 ...

  4. JavaScript进阶系列01,函数的声明,函数参数,函数闭包

    本篇主要体验JavaScript函数的声明.函数参数以及函数闭包. □ 函数的声明 ※ 声明全局函数 通常这样声明函数: function doSth() { alert("可以在任何时候调 ...

  5. javascript进阶课程--第三章--匿名函数和闭包

    javascript进阶课程--第三章--匿名函数和闭包 一.总结 二.学习要点 掌握匿名函数和闭包的应用 三.匿名函数和闭包 匿名函数 没有函数名字的函数 单独的匿名函数是无法运行和调用的 可以把匿 ...

  6. javascript进阶教程第三章--匿名和闭包--案例实战

    javascript进阶教程第三章--匿名和闭包--案例实战 一.学习任务 通过几个小练习回顾学过的知识点 二.实例 练习1: 实例描述:打开页面后规定时间内弹出一个新窗口,新窗口指定时间后自动关闭. ...

  7. 结合个人经历总结的前端入门方法 (转自https://github.com/qiu-deqing/FE-learning)

    结合个人经历总结的前端入门方法 (https://github.com/qiu-deqing/FE-learning),里面有很详细的介绍. 之前一直想学习前端的,都不知道怎么下手都一年了啥也没学到, ...

  8. 2019年Web前端入门的自学路线

    本文最初发表于博客园,并在GitHub上持续更新前端的系列文章.欢迎在GitHub上关注我,一起入门和进阶前端. 以下是正文.本文内容不定期更新. 我前几天写过一篇文章:<裸辞两个月,海投一个月 ...

  9. 深入理解javascript原型和闭包系列

    从下面目录中可以看到,本系列有16篇文章,外加两篇后补的,一共18篇文章.写了半个月,从9月17号开始写的.每篇文章更新时,读者的反馈还是可以的,虽然不至于上头条,但是也算是中规中矩,有看的人,也有评 ...

  10. 4、JavaScript进阶篇①——基础语法

    一.认识JS 你知道吗,Web前端开发师需要掌握什么技术?也许你已经了解HTML标记(也称为结构),知道了CSS样式(也称为表示),会使用HTML+CSS创建一个漂亮的页面,但这还不够,它只是静态页面 ...

随机推荐

  1. Vs 开发时无法断点问题

    1.清除解决方案 2.重新编译 3.删除项目目录下的obj 和 bin 4.在vs中配置 工具--项目--调试--去除勾选 要求源文件与原始版本完全匹配 关于调试问题 1.关闭诊断工具, 工具 =&g ...

  2. 急急如律令!火速搭建一个C#即时通信系统!(附源码分享——高度可移植!)

    (2016年3月更:由于后来了解到GGTalk开源即时通讯系统,因此直接采用了该资源用于项目开发,在此对作者表示由衷的感谢!) —————————————————————————————————— 人 ...

  3. 使用Sublime Text 或 vs2017开发Node.js程序

    在学习一门开发语言时,为了从简单的方式入手,有时候直接用Notepad开始敲代码.曾经我也这样干过,这样做简洁而不简单啊! 随着时间的流逝,人也变得懒惰起来,做事前总是想借助一些工具来搞事情.< ...

  4. FFmpeg命令行工具学习(三):媒体文件转换工具ffmpeg

    一.简述 ffmpeg是一个非常强大的工具,它可以转换任何格式的媒体文件,并且还可以用自己的AudioFilter以及VideoFilter进行处理和编辑.有了它,我们就可以对媒体文件做很多我们想做的 ...

  5. Javascript高级编程学习笔记(1)—— JS简介

    此系列文章,用于记录所学,如有错误欢迎指出. Javascript组成 1.核心(ECMAScript) 2.文档对象模型(DOM) 3.浏览器对象模型(BOM) 1.核心(ECMAScript) E ...

  6. Python面试真题第四节

    81.举例说明SQL注入和解决办法 82.s="info:xiaoZhang 33 shandong",用正则切分字符串输出['info', 'xiaoZhang', '33', ...

  7. Visual Studio 2010软件安装教程

    链接:https://pan.baidu.com/s/10FeLlKpzFcb9yUjm3ZECsg 提取码:pup1 复制这段内容后打开百度网盘手机App,操作更方便哦 1.右击软件压缩包,选择解压 ...

  8. Python内置函数(32)——input

    英文文档: input([prompt]) If the prompt argument is present, it is written to standard output without a ...

  9. Java核心技术及面试指南的视频讲解和代码下载位置

    都是百度云盘,均无密码 代码下载位置: https://pan.baidu.com/s/1I44ob0vygMxvmj2BoNioAQ 视频讲解位置: https://pan.baidu.com/s/ ...

  10. NodeJs安装步骤与淘宝镜像

    dir 列目录 lscd 路径 切换路径 cdmd 文件夹名 创建一个空文件夹 mdC: 切换盘符 cls 清屏 clear ping ip/网址 网络测试ipconfig -all 查看网络连接信息 ...