作为八股文中最常见的一个问题,闭包一开始对小白来说是比较难理解的一个部分,虽然平时可能使用闭包的地方比较少,但是确实面试中热门的问题。本文参考地址

☀️ 什么是闭包

一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来----MDN

简单来说:闭包存在俩个特点

  1. 在一个函数中返回另外一个函数
  2. 内函数使用了外函数的参数或者是变量
    最常见的写法是长这样子的
function outSide() {var name = "catch me"; return function inSide() { console.log(name);}
}
const inSides = outSide()
inSides();

outSide函数中有一个局部变量name和一个inSide函数,inSide属于outSide的内部函数,但是inSide中没有自己的变量,但他可以访问到外部函数outSide的变量。当outSide函数执行完之后,一般来说一个函数中的局部变量执行完就无法访问了。然而在inSide仍然能拿到变量的值,所以闭包情况与此不同,执行上下文被销毁,但创建时所在词法环境依然存在,变量仍然能够保存下来。

☀️ 闭包的使用场景

一个简单的应用场景,该方法使用了一个闭包返回设置fontsize 的不同大小,我们可以通过设置不同的参数在同一个闭包上,返回同一函数不同参数的几种方法,这样就可以批量的产出我们需要用到的函数

function makeSizer(size) {return function() {document.body.style.fontSize = size + 'px';};
}var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

1.柯里化函数

柯里化主要是对相同参数的函数进行复用,比如下图可以实现一个计算面积大小的函数。

function getArea(width, height) {return width * height
}
// 假设需要计算几个宽度不变高度发生变化的面积,这样重复的写会比较麻烦
const area1 = getArea(10, 20)
const area2 = getArea(10, 30)
const area3 = getArea(10, 40)// 使用闭包柯里化固定一个宽度参数去计算面积
function getArea(width) {return height => {return width * height}
}const getTenWidthArea = getArea(10)
// 生成一个计算宽度为10高度任意变化的函数
const area1 = getTenWidthArea(20)
const area2 = getTenWidthArea(30)// 生成一个计算宽度为20高度任意变化的函数
const getTwentyWidthArea = getArea(20)

2.模拟私有方法

JavaScript中,没有支持声明私有变量,闭包可以用来模拟私有方法

在下面这段代码中,我们需要生成多个计数器,多个计数器之间互不干扰,各自执行自己的计数加减,通过闭包的方式可以将计算的加减方法变成每个计数器的公有函数法,并在其中访问计数器的私有方法和变量,这样各个计数器之间不会影响到。

var Counter = (function() {var privateCounter = 0;function changeBy(val) {privateCounter += val;}return {increment: function() {changeBy(1);},decrement: function() {changeBy(-1);},value: function() {return privateCounter;}}
})();var Counter1 = makeCounter();
var Counter2 = makeCounter();
console.log(Counter1.value()); /* logs 0 */
Counter1.increment();
Counter1.increment();
console.log(Counter1.value()); /* logs 2 */
Counter1.decrement();
console.log(Counter1.value()); /* logs 1 */
console.log(Counter2.value()); /* logs 0 */

在方法执行的过程中,改变这个私有变量的值,闭包的词法环境被改变,但不会影响另一个闭包中的变量,因为两个闭包之间都是私有的。

性能优化

function MyObject(name, message) {this.name = name.toString();this.message = message.toString();this.getName = function() {return this.name;};this.getMessage = function() {return this.message;};
}

上面的闭包写法在日常中可能并不太经常存在,由于闭包对性能方面会有一定的影响,所以一般是比较少用到闭包的,对于上面的写法可以通过关联对象原型的方法进行使用,比如下面这段例子,变量已经通过this的方式放在对象中,所以我们需要用到的方法可以直接在原型上进行绑定。

function MyObject(name, message) {this.name = name.toString();this.message = message.toString();
}
MyObject.prototype.getName = function() {return this.name;
};
MyObject.prototype.getMessage = function() {return this.message;
};

作用域的认识

开始的时候我们说到闭包是在一个函数中返回另一个函数,内部函数可以访问到外部函数的作用域,那么这个作用域是是指什么呢,作用域其实就是一段段的代码块,比如声明变量和方法或者执行方法的代码,处在不同位置会成为不同的作用域,一般分为三块作用域

  • 全局作用域
  • 函数作用域
  • 块级作用域

全局作用域

像以下这种变量、函数、函数执行都在全局作用下,不在函数中,或者一个大括号中声明的,则视为处在全局作用域中

// 全局变量
var greeting = 'Hello World!';
function greet() {console.log(greeting);
}
// 打印 'Hello World!'
greet();

函数作用域

这个比较简单,在函数中的代码都处在函数作用域中,外部访问不到,进行访问的时候会报错,通常也叫做局部作用域

function greet() {var greeting = 'Hello World!';console.log(greeting);
}
// 打印 'Hello World!'
greet();
// 报错: Uncaught ReferenceError: greeting is not defined
console.log(greeting);

块级作用域

块级作用域主要保存着let和const声明的变量,主要表现为一个大括号,大括号中let和const声明的变量视为存在于块级作用域中,在大括号外无法访问,但是var声明的为全局变量,会造成变量提升,在大括号外仍然是可以访问的到的。

{// 块级作用域中的变量let greeting = 'Hello World!';var lang = 'English';console.log(greeting); // Prints 'Hello World!'
}
// 变量 'English'
console.log(lang);
// 报错:Uncaught ReferenceError: greeting is not defined
console.log(greeting);

三分钟简单了解闭包及作用域相关推荐

  1. 三分钟简单阐释计算机发展史,致写文案的你:三分钟快速看完这本书的精髓

    图片发自简书App 刚刚看完了这本<尖叫感:互联网文案创意思维与写作技巧>. 阅读感不是很好.但是里面的细节还是有很多可学习的地方. 首先明白一个道理: 文案无须转型,只需生长,互联网改变 ...

  2. 三分钟简单了解SaaS、PaaS、IaaS,别再企业上云时犯糊涂!

    云计算.云服务.云平台--现在"云"已成了一个家喻户晓的概念,但PaaS, IaaS 和SaaS的区别估计还没有那么多的人分得清,下面就分别向大家普及一下它们的基本概念: SaaS ...

  3. 三分钟简单了解VR、AR、MR、XR是什么

    目录 一.VR虚拟现实( Virtual Reality ) 二.AR增强现实(Augmented Reality) 三.MR 混合现实(Mixed Reality) 四.XR扩展现实( Extend ...

  4. 简单三分钟,本地搭建k8s

    使用 minikube 在本地搭建 k8s 已经比以前要简单很多了.本文,我们通过简短的三分钟来重现一下在本地搭建 k8s 实验环境的步骤. 下载 Minikube 首先,你可能会考虑从官网下载 mi ...

  5. 建网站的最简单方法(三分钟带后台)

    建网站的最简单方法(三分钟带后台) 准备材料 服务器或者本地环境 安装过程 准备材料 织梦二次开发模板或者Ecshop二次开发模板等(我以织梦为例讲解) 如果是本地需要下载ComsenzEXP或者Wa ...

  6. unity3d 三分钟实现简单的赛车漂移

    http://www.cnblogs.com/shenggege/p/5393815.html 提到赛车游戏,大家最关心的应该就是漂移吧?! 从学unity开始,我就一直在断断续续的研究赛车 因为自己 ...

  7. 闭包,作用域链,垃圾回收,内存泄露

    关于闭包,我翻了几遍书,看了几遍视频,查了一些资料,可是还是迷迷糊糊的,干脆自己动手来个总结吧 !欢迎指正... (- o -)~zZ 1. 什么是闭包? 来看一些关于闭包的定义: 闭包是指有权访问另 ...

  8. 翻译:三分钟学懂JSON

    Understanding JSON: the 3 minute lesson 三分钟学懂JSON Two months ago you'd never heard of JSONIf you are ...

  9. 25、搞懂闭包、作用域、执行期上下文(VO、AO)、作用域链

    25.1 闭包 闭包是指有权访问另一个函数作用域中的变量的函数--js高程 简单来说闭包就是函数 A 内部有一个函数 B,函数 B 可以访问到函数 A 中的变量,那么函数 B 就是闭包. 25.2 作 ...

最新文章

  1. 【笔记】C++ 简化位图图像操作
  2. 《编译与反编译技术》——第一章 引论 1.1节编译器与解释器
  3. STM32的ISP升级详解
  4. 永磁交流伺服电机的工作原理与更换新编码器后的常规零位校正方法
  5. 天天说常识推理,究竟常识是什么?
  6. Scrapy 爬虫教程导航
  7. [深度学习-NLP]什么是Self-attention, Muti-attention和Transformer
  8. 95 后程序员一出校门就拿年薪 30多万?
  9. 计算机专业认证协会,2017计算机类专业认证委员会工作总结会在京召开
  10. 超级简单的html转换为pdf格式方法
  11. 数字/模拟信号中带宽的含义
  12. 菜菜的刷题日记 | 66.加一 Plus One
  13. 【Windows】电脑蓝牙突然无法使用,解决办法来了
  14. 如何下载B站(bilibili)的视频
  15. C++如何实现二进制数据存储为灰度图
  16. 静态路由基础,扩展配置 超详细滴
  17. 【C库函数】strlen函数详解
  18. 2020年新版Java学习路线图最全更新!囊括史上最全面104个知识点
  19. python实现两张图片拼接(身份证正反面图片拼接)
  20. 软件设计师教程-数据库

热门文章

  1. 数据调度系统中有向无环图的无环检测
  2. 在自己网页浏览器端通过websocket接入海康摄像头实时视频
  3. 对jvm的学习--简章
  4. 在IDEA中.ignore插件的安装与使用
  5. 南邮 OJ 2083 法师
  6. M1 Mac 重新安装 macOS 时收到个性化错误的解决办法
  7. 子沐课堂——学员管理系统(前期准备+Model建立)
  8. WinForm界面开发第三方控件——支持Office 2019 Light主题
  9. 前后端分离时使用thinkphp5.1的captcha验证码
  10. Windows11 安装Oracle 12c