当前位置: 首页 > news >正文

【面试题】对闭包的理解?什么是闭包?

大厂面试题分享 面试题库

后端面试题库 (面试必备) 推荐:★★★★★

地址:前端面试题库

闭包的背景

由于js中只有两种作用域,全局作用域和函数作用域,而在开发场景下,将变量暴露在全局作用域下的时候,是一件非常危险的事情,特别是在团队协同开发的时候,变量的值会被无意篡改,并且极难调试分析。这样的情况下,闭包将变量封装在局部的函数作用域中,是一种非常合适的做法,这样规避掉了被其他代码干扰的情况。

闭包的使用

下面是一种最简单直接的闭包示例

//妈妈本体functionmother(){//口袋里的总钱数let money = 100//消费行为returnfunction (pay){//返回剩余钱数return money - pay}
}
//为儿子消费let payForSon = mother()
//打印最后的剩余钱数console.log(payForSon(5))
复制代码

为了便于理解,我们将外部函数比喻为妈妈本体,里面保存着总钱数这个变量和消费这个行为,通过创建为儿子消费的这个行为对象,然后执行这个行为花费5元,返回剩余的95元。

这个就是为了将变量money保存在mother本体内而避免暴露在外部的全局环境作用域中,只能通过mother()创建消费行为来影响money这个变量。

由此可以归纳总结使用闭包的三个步骤

  1. 用外层函数包裹变量,函数;

  1. 外层函数返回内层函数;

  1. 外部用变量保存外部函数返回的内层函数

目的是为了形成一个专属的变量,只在专属的作用域中操作。

上述的闭包代码示例中,有一个缺陷的场景是,在后续不需要money变量的情况下,没有释放该变量,造成内存泄露。原因是payForSon这个函数的作用域链引用着money对象,解决的办法是将payForSon = null就可以释放方法作用域,进而解除对money的引用,最后释放money变量。

闭包的扩展

函数柯里化


在开发的场景中,有时需要通过闭包来实现函数的柯里化调用。调用示例如下

alert(add(1)(2)(3))
复制代码

这种连续的传参调用函数,叫做函数柯里化。

通过闭包的实现方式如下

functionadd(a){//保存第一个参数let sum = afunctiontmp(b){//从第二个函数开始递加sum = sum + b//返回tmp,让后续可以继续传参执行return tmp}tmp.toString = function(){return sum}//返回加法函数return tmp
}
alert(add(1)(2)(3))
复制代码

下面我们来一步步分析,

  1. add(1)执行时,保存第一个参数到sum变量中,返回tmp函数

  1. add(1)(2)执行等于tmp(2),将2的值加到了变量sum上,返回tmp函数本身

  1. add(1)(2)(3)执行等同于上述步骤的加到比变量sum上,返回tmp函数本身

  1. alert(add(1)(2)(3))执行时,alert需要将值转为string显示,最后的tmp函数执行tmp.toString,返回sum的值。

矩阵点击应用


该例子的demo代码在我的github上,可以自行取阅

需求:在一个4*4的矩阵方块中,实现点击每个按钮时记录下各自的点击次数,相互之间互不干扰。

思路:在按钮事件中使用闭包,创建独立的存储变量空间。

注意:下列的方案1到方案3是逐次演进的优化方案,需要按照方案标号的次序逐层理解,更有利于理解最终的优化方案

方案1

<div id="container"></div>
...
let container = document.getElementById('container')
for (let r = 0; r < arr.length; r++) {for (let c = 0; c < arr[r].length; c++) {let cell = document.createElement('div')cell.innerHTML = `(${r},${c})`container.append(cell)cell.onclick = (function () {let n = 0return function () {n++cell.innerHTML = `点${n}`}})()}
}
复制代码

在每个按钮上通过onclick绑定闭包方法,存储操作独立的n变量,这样就可以单独记录每个按钮的点击次数

缺点:这样做有一个不足的地方是,外部无法获取内部的n变量,不能实现与外部的交互,比如按钮间的相互影响。

方案2

为了改善方案1的缺点,我们引入外部数据arr来操作管控按钮点击数。 代码示例如下:

let arr = [[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],]
let container = document.getElementById('container')
for (let r = 0; r < arr.length; r++) {for (let c = 0; c < arr[r].length; c++) {let cell = document.createElement('div')cell.innerHTML = `(${r},${c})`container.append(cell)cell.onclick = (function (r, c) {return function () {arr[r][c]++cell.innerHTML = `点${arr[r][c]}`}})(r, c)}
}
复制代码

参照方案1 ,改动点包含两个

  • 新增arr二维数组来记录点击数,这样可以达到与外部交互的目的

  • onclick绑定的事件新增r,c两个参数,并且执行时传参进入,这样就可以把行列参数传递到方法内部(onclick的执行环境作用域与r,c所在的环境不一致,所以无法直接使用)

这样改进完以后,外部可以通过操作arr来与每个按钮的点击次数进行交互。

缺点:这样会将arr暴露在全局作用域下(可以在console控制台访问到),很容易被其他人或者模块误操作,也不利于封装

方案3

基于方案2的改进实现为,用一个立即执行的函数包裹住整个执行代码,这样就构建了一个函数作用域来封装arr变量为私有。代码如下:

(function () {let arr = [[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],]let container = document.getElementById('container')for (let r = 0; r < arr.length; r++) {for (let c = 0; c < arr[r].length; c++) {let cell = document.createElement('div')cell.innerHTML = `(${r},${c})`container.append(cell)cell.onclick = (function (r, c) {return function () {arr[r][c]++cell.innerHTML = `点${arr[r][c]}`}})(r, c)}}})()
复制代码

这样一个相对完整的按钮点击次数的方案就完成了。

使用call实现bind


这个需要有call和bind的使用知识的前提,可以自行百度哈

废话不多说,直接上代码

Function.prototype.bind = function(obj){console.log('调用自定义bind函数');//保存当前函数对象let fun = this//去除第一个obj参数,并且转换为js数组let outerArg = Array.prototype.slice.call(arguments,1)returnfunction(){//将arguments转为js数组let innerArg = Array.prototype.slice.call(arguments)//汇总所有参数let totalArg = outerArg.concat(innerArg)//调用外部保存的函数,并且传参fun.call(obj,...totalArg)}
}//调用示例let zhangsan = {name:'wawawa'}
functiontotal(s1,s2){console.log(this.name + s1 + s2);
}
let bindTotal = total.bind(zhangsan,100)
bindTotal(200)
复制代码

重写函数类的bind函数,

  1. 先将函数对象(也就是下面示例中的total函数)保存在fun变量中,等于闭包外层保存了fun,obj以及其他绑定的参数(由于arguments是类数组对象,需要转换为数组,且去除第一个函数obj);

  1. 然后返回匿名函数,在匿名函数中,将外部和内部的参数进行转换和拼接;

  1. 最后通过fun.call(obj,...totalArg),调用保存的函数对象fun,并且通过call来实现传递绑定的作用域obj,和其他参数totalArg

注意:

  • arguments是类数组对象,不能直接使用数组方法,需要转化为数组操作

  • 外层函数arguments转化时,需要剔除掉obj,因为下面的fun.call需要单独传递obj作为函数作用域

  • totalArg传递给call函数时,需要通过...语法糖摊开数组

大厂面试题分享 面试题库

后端面试题库 (面试必备) 推荐:★★★★★

地址:前端面试题库

相关文章:

【面试题】对闭包的理解?什么是闭包?

大厂面试题分享 面试题库后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★地址&#xff1a;前端面试题库闭包的背景由于js中只有两种作用域&#xff0c;全局作用域和函数作用域&#xff0c;而在开发场景下&#xff0c;将变量暴露在全局作用域下的时候…...

笔试题-2023-乐鑫-数字IC设计【纯净题目版】

回到首页:2023 数字IC设计秋招复盘——数十家公司笔试题、面试实录 推荐内容:数字IC设计学习比较实用的资料推荐 题目背景 笔试时间:2022.09.01应聘岗位:数字IC设计工程师笔试时长:60min笔试平台:nowcoder牛客网题目类型:单选题(2道)、不定项选择题(7题)、问答题(…...

antd日期组件时间范围动态跟随

这周遇到了一个很诡异但又很合理的需求。掉了一周头发&#xff0c;死了很多脑细胞终于上线了。必须总结一下&#xff0c;不然对不起自己哈哈哈。 一、需求描述 默认当前日期时间不可清空。 功能 默认时间如下&#xff1a; 目的&#xff1a;将时间改为 2014-08-01 ~ 2014-08…...

mysql一条sql语句的执行过程

sql的具体执行过程 客户端发送一条查询给服务器服务器下先检查查询缓存&#xff0c;如果命中了缓存&#xff0c;返回缓存中的结果否则就需要服务器端进行sql的解析、预处理&#xff0c;再由优化器生成对应的执行计划根据执行计划&#xff0c;调用存储引擎的api来执行查询将结果…...

SaaS是什么,和多租户有什么关系?

空间数据又称几何数据&#xff0c;用来表示物体的位置&#xff0c;形态&#xff0c;大小分布等各方面的信息&#xff0c;是对现实世界中存在的具有定位意义的事物和现象的定量描述。 多租户是SaaS领域特有的产物。 SaaS服务是部署在云上的&#xff0c;客户可以按需购买&#…...

C语言---字符串函数总结

&#x1f680;write in front&#x1f680; &#x1f4dd;个人主页&#xff1a;认真写博客的夏目浅石. &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f4e3;系列专栏&#xff1a;夏目的C语言宝藏 &#x1f4ac;总结&#xff1a;希望你看完之…...

MySQL-表的基本操作

一、创建数据表创建数据表是指在已经创建好的数据库中建立新表。创建数据表的过程是规定数据列的属性的过程&#xff0c;同时也是实施数据完整性约束的过程。创建表之前应先使用语句{use 数据库名} 进入到指定的数据库&#xff0c;再执行表操作。创建表语法:CREATE TABLE <表…...

开篇之作—闲聊几句AUTOSAR

背景信息 步入职场已有些许年头,遇到过不少的人,经历过不算多的事情,也走过一些地方。现在坐下来想想,觉得一路走过总是行色匆匆,都来不及停下来驻足路边的风景,抑或是回头看看身后的精彩。 现在有些庆幸的是,加入了这个汽车这个行业,从事着汽车电子开发领域,也因此…...

02- 天池工业蒸汽量项目实战 (项目二)

忽略警告: warnings.filterwarnings("ignore") import warnings warnings.filterwarnings("ignore") 读取文件格式: pd.read_csv(train_data_file, sep\t) # 注意sep 是 , , 还是\ttrain_data.info() # 查看是否存在空数据及数据类型train_data.desc…...

LeetCode-111. 二叉树的最小深度

目录题目分析递归法题目来源111. 二叉树的最小深度题目分析 这道题目容易联想到104题的最大深度&#xff0c;把代码搬过来 class Solution {public int minDepth(TreeNode root) {return dfs(root);}public static int dfs(TreeNode root){if(root null){return 0;}int left…...

git常用命令

&#xff08;一&#xff09;克隆代码&#xff08;clone&#xff09;&#xff1a;将远程仓库代码克隆到本地仓库 克隆远程仓库某个分支 git clone -b 远程分支名称 https://github.com/master/master.git 本地文件名称 克隆远程仓库默认分支 git clone https://github.com/mas…...

2022年12月电子学会Python等级考试试卷(一级)答案解析

青少年软件编程&#xff08;Python&#xff09;等级考试试卷&#xff08;一级&#xff09; 一、单选题(共25题&#xff0c;共50分) 1. 关于Python语言的注释&#xff0c;以下选项中描述错误的是&#xff1f;&#xff08; &#xff09; A. Python语言有两种注释方式&…...

大数据未来会如何发展

大数据应用的重要性&#xff0c;自全国提出“数据中国”的概念以来&#xff0c;我们周围默默地在发挥作用的大数据逐渐深入人们的心中&#xff0c;大数据的应用也越来越广泛&#xff0c;具体到金融、汽车、餐饮、电信、能源、体育和娱乐等领域 为什么大数据技术那么火&#xf…...

2022黑马Redis跟学笔记.基础篇(一)

2022黑马Redis跟学笔记.基础篇 一1.Redis入门1.1.认识NoSQL1.1.1.结构化与非结构化1.1.2.关联和非关联1.1.3.查询方式1.1.4.事务1.1.5.总结1.2.认识Redis1.3.安装Redis步骤一&#xff1a;安装Redis依赖步骤二&#xff1a;上传安装包并解压步骤三&#xff1a;启动(1).默认启动(2…...

【Spring(十一)】万字带你深入学习面向切面编程AOP

文章目录前言AOP简介AOP入门案例AOP工作流程AOP切入点表达式AOP通知类型AOP通知获取数据总结前言 今天我们来学习AOP,在最初我们学习Spring时说过Spring的两大特征&#xff0c;一个是IOC,一个是AOP,我们现在要学习的就是这个AOP。 AOP简介 AOP:面向切面编程,一种编程范式&#…...

基于Java+SpringBoot+Vue+uniapp前后端分离图书阅读系统设计与实现

博主介绍&#xff1a;✌全网粉丝3W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建、毕业项目实战、项目定制✌ 博主作品&#xff1a;《微服务实战》专栏是本人的实战经验总结&#xff0c;《S…...

2021年新公开工业控制系统严重漏洞汇总

声明 本文是学习ITOT一体化工业信息安全态势报告&#xff08;2019&#xff09;. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 工业互联网安全威胁 2021年新公开工业控制系统严重漏洞 缓冲区溢出漏洞 缓冲区溢出&#xff08;buffer overflow&…...

Canvas鼠标滚轮缩放以及画布拖动(图文并茂版)

Canvas鼠标滚轮缩放以及画布拖动 本文会带大家认识Canvas中常用的坐标变换方法 translate 和 scale&#xff0c;并结合这两个方法&#xff0c;实现鼠标滚轮缩放以及画布拖动功能。 Canvas的坐标变换 Canvas 绘图的缩放以及画布拖动主要通过 CanvasRenderingContext2D 提供的 …...

[ECCV 2020] FGVC via progressive multi-granularity training of jigsaw patches

Contents IntroductionProgressive Multi-Granularity (PMG) training frameworkExperimentsReferencesIntroduction 不同于显式地寻找特征显著区域并抽取其特征,作者充分利用了 CNN 不同 stage 输出的特征图的语义粒度信息,并使用 Jigsaw Puzzle Generator 进行数据增强来帮…...

Python推导式

列表&#xff08;list&#xff09;推导式 [remove for source in xx_list]或者[remove for source in xx_list if condition] 实例&#xff1a; names[Bob,Mark,Mausk,Johndan,Wendy] new_names[name.upper() for name in names if len(name)<5] print(new_names)即迭代列…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验

Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...