须知:我们在学一个新的东西的时候,例如我们第一次见到电饭煲,我们要知道什么是电饭煲(定义),知道电饭煲有什么用(作用),知道电饭煲怎么用(用法),如何用电饭煲煮一顿好吃的饭(实践),做出来之后你就真正懂了电饭煲。学习闭包的过程亦是如此,所以我们不用害怕学习新事物!

闭包专题

  • 1.闭包的定义
  • 2.闭包的作用
  • 3.闭包的用法
  • 4.闭包的应用
  • 5.经典面试题
  • 6.闭包的缺点

1.闭包的定义

  • 定义:闭包是指有权访问另一个函数作用域中的变量的一个函数。简单的说,你可以认为闭包是一个特别的函数,他能够读取其他函数内部变量的函数。
  • 自由变量:在当前作用域没有定义,在父作用域及以上定义了,他会一层一层的往上找。闭包的中那个没有被回收的变量就是自由变量。自由变量是在函数定义的那里一层层的往上找。

2.闭包的作用

  • 作用:正常的函数,在执行完之后,函数里面声明的变量就会被垃圾回收处理掉。但是闭包可以让一个函数作用域中的变量,在执行完之后依旧没有被垃圾回收处理掉。

3.闭包的用法

闭包经典例子:

    function a() {var name = '小明'return function () {console.log(name)}}var b = a()b() // 打印出:小明

注:闭包一般写成自调用函数的形式,以上的例子可以写成这样:

    var b = (function () {var name = '小明'return function () {console.log(name)}})()b()   // 打印出:小明

我们可以参照闭包的定义和闭包的作用来解析这个例子: 例子中的name变量是函数a的局部变量(就是只能在函数a中才能调用的变量),在var b = a()这段代码执行完之后,就是a函数执行完之后,name变量应该被垃圾回收机制回收掉,但是在调用b方法之后,控制台打印出小明,说明name变量并没有回收。说明b函数就是一个闭包。

总结闭包的特征:

  • 函数中嵌套函数
  • 函数a的返回的是一个函数
  • 函数a中返回的函数 调用 函数a中的声明的变量

自调用函数:顾名思义,就是函数自己调用自己。举个例子:

 // 正常调用函数function a() {console.log("haha")}a()// 自调用(function () {console.log("haha")})()

自调用函数解析:我们可以把自调用想象成这种形式:(函数)()。第一个括号是为了把函数包裹起来,当作一个整体,第二个括号是为了调用函数。我们调用函数都是使用()来调用的。例如:a(),就是调用a函数。

4.闭包的应用

应用场景1:当我们需要把网页上的a标签都添加点击事件,点击a标签打印对应的下标。
html代码:

<!DOCTYPE html>
<html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title></title>
</head><body><a href="#">0</a><a href="#">1</a><a href="#">2</a><a href="#">3</a><a href="#">4</a>
</body></html>

可能有一部分人使用for循环来添加:

 // 获取所有a标签var a = document.querySelectorAll('a')  // 循环添加点击事件// a.length的值是:5for (var i = 0; i < a.length; i++) {  a[i].onclick = function () {   console.log(i)}}

这时候运行起来,你发现无论你点击哪个标签,打印出来的都是:5
原因:因为for循环在页面加载完就执行完了,所以i的值变成了5。当你点击的时候,点击的函数就回去一层一层的往上找,发现i的值是5,就打印5了。
这时,我们就可以用闭包来解决这个bug

    var a = document.querySelectorAll('a')for (var i = 0; i < a.length; i++) {a[i].onclick = (function () {var tem = ireturn function () {console.log(tem)}})()}

解析:for循环每执行一遍的时候,自调用函数就会将对应的i赋值给tem,然后返回一个函数,返回的函数就是对应的点击事件函数。因为闭包的缘故,tem在函数调用完之后不会被回收,所以能够被正确调用。

或者写成这种形式:

    var a = document.querySelectorAll('a')for (var i = 0; i < a.length; i++) {a[i].onclick = (function (i) {return function () {console.log(i)}})(i)}

解析:这种形式,就是把i以参数的形式传给自调用函数,就是省去声明tem变量的步骤。

应用场景2:在一个页面实现多个计数器。
html代码:

<!DOCTYPE html>
<html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title></title>
</head><body><p id="text1">0</p><button id="btn1">点击+1</button><p id="text2">0</p><button id="btn2">点击+1</button>
</body>
</html>

很多人选择这样的方式来实现:

    var text1 = document.querySelector('#text1')var btn1 = document.querySelector('#btn1')var text2 = document.querySelector('#text2')var btn2 = document.querySelector('#btn2')var count1 = 0var count2 = 0btn1.addEventListener('click', function () {count1++text1.innerHTML = count1})btn2.addEventListener('click', function () {count2++text2.innerHTML = count2})

如果使用闭包来实现,我们可以减少全局变量的使用,而且方便维护:

    var text1 = document.querySelector('#text1')var btn1 = document.querySelector('#btn1')var text2 = document.querySelector('#text2')var btn2 = document.querySelector('#btn2')function addCount() {var count = 0return function (text) {count++text.innerHTML = count}}var add1 = addCount() //生成按钮1的计数器btn2.onclick = function () {add2(text1)}var add2 = addCount()  //生成按钮2的计数器btn2.onclick = function () {add2(text2)}

5.经典面试题

经典闭包面试题:

for (var i = 0; i < 4; i++) {setTimeout(function() {console.log(i);}, 300);
}

第一眼看到这个题的时候,我相信很多同学都会觉得打印出来的是:0,1,2,3。
但是这道题正确答案是:4,4,4,4。这道题跟我们上面的一个例子有点相似,但是这道题还涉及一个异步函数的知识点。

js代码执行顺序:没有异步任务时:代码从上往下执行。有异步任务是:代码先执行主线程,执行完之后才执行异步任务。setTimeout()是一个异步函数,所以这道题的代码执行顺序时,先执行for循环完,此时i的值时4,再执行setTimeout种的方法。以至于打印出来的都是4。

使用闭包来让这道题打印出来的是:0,1,2,3

for (var i = 0; i < 4; i++) {setTimeout((function (i) {return function () {console.log(i)}})(i), 300);}// 或者写成这样for (var i = 0; i < 4; i++) {setTimeout((function () {var tem = ireturn function () {console.log(tem)}})(), 300);}

由于for循环没啥变化,变化的是setTimeout的函数。我们来看一下setTimeout种的内容:因为setTimeout函数需要两个参数,第一个参数是一个函数,第二个是间隔时间。我们来看一下第一个参数:因为它是需要一个函数的类型,所以我们使用了自调用函数,让他自动执行完之后返回一个函数类型来给我们作为参数(注:自动执行函数是跟for循环一起执行的)。这里就是使用闭包的作用,延申了i作用域,让他在自调用函数执行完之后没有被回收。

6.闭包的缺点

因为变量没有被回收,所以内存中一直存在,耗费内存。

js高级篇:什么是闭包?闭包有什么用?面试时如何处理闭包问题?相关推荐

  1. Node.js 高级篇(六):手把手教你使用和理解 Multer 实现文件上传,包懂 O(∩_∩)O~

    文章目录 前端 界面 代码 服务端 启动服务 文件信息 Multer 中间件 Multer(options) .single(fieldname) .array(fieldname[, maxCoun ...

  2. 前端之路(JS高级篇)

    面向对象编程 什么是对象?? (1) 从视觉角度 : 对象是单个事物的抽象. 一本书.一个人都可以是对象,一张网页.一个与远程服务器的连接也可以是对象.当实物被抽象成对象,实物之间的关系就变成了对象之 ...

  3. (一)JS 基础篇—基础知识总结

    ⛺️ 欢迎大家拜访我的:个人博客 ⛽️ 前端加油站之[JavaScript]⛽️ 内容 地址 (一)JS 基础篇-基础知识总结 ⛳️ [快来点点我 ~] (二)JS 基础篇-函数与作用域 ⛳️ [快来 ...

  4. JS 高级(二)闭包、封装

    目录 一.闭包 二.面向对象 1. 封装 一.闭包 全局变量和局部变量在使用的过程中都各有优点,但它们也都有着自己的不足之处.全局变量的好处是可以被重用,但是极易被污染(注意一般公司中禁止使用一切形式 ...

  5. js高级第四天(apply call bind以及闭包)

    apply和call方法的使用 作用:可以改变this指向 //apply和call都可以改变this的指向//函数的调用,改变this的指向// function f1(x,y) {// conso ...

  6. 妙味课堂ajax教程,妙味课堂JS高级专题篇视频资料分享

    <妙味课堂JS高级专题篇视频教程>将向大家详细介绍javascript,javascript是一种直译式脚本语言,也是一种广泛用于客户端Web开发的脚本语言.目前,被数百万计的网页用来改进 ...

  7. JavaScript 进阶知识 - 高级篇

    JS高级 前言 经过前面几篇文章的学习,相信大家已经对js有了大部分的理解了,但是要想真正的掌握好js,本篇才是关键.由于js高级阶段的知识点比较难理解,所以本篇文章花了大量的时间去理思路,有可能有一 ...

  8. JavaScript高级知识汇总(高级篇)

    目录 JavaScript高级知识总结(高级篇) 一.深入基础 1.1数据类型 1.2数据变量与内存 1.3对象 1.4函数 回调函数 1.5 IIFE 1.6函数中的this 二.函数高级 2.1原 ...

  9. 前端面试题目汇总摘录(JS 基础篇 —— 2018.11.01更新)

    温故而知新,保持空杯心态 JS 基础 JavaScript 的 typeof 返回那些数据类型 object number function boolean undefined string type ...

最新文章

  1. mount windows目录
  2. SqlServer中BCP导出数据的方法
  3. java设计模式迭代器模式_迭代器设计模式示例
  4. LeetCode 811. 子域名访问计数
  5. 【2019.08.21】2019杭电多校第十场
  6. MongoDB 主从架构
  7. 写代码如坐禅:你是哪一类程序员
  8. 计算机word表格三线形,word中制作三线表格的四种方法
  9. 因子分析(FA)算法简述
  10. 西门子PLC面向对象编程
  11. 【bat】 计算机清理原理,使用bat脚本清理系统垃圾的方法
  12. vt-d 基本操作流程
  13. win10系统安装和优化
  14. 人员玩手机离岗识别检测系统 yolov5
  15. Java直接控制打印机打印
  16. python数据分析实战之阿里巴巴股票行情分析
  17. poj 1187 陨石的秘密
  18. 使用Android Studio开发一个简易的音乐播放器
  19. 北航操作系统课程-第一次作业-操作系统引论1
  20. 很不错的免费电影网站中国影视库mdbchina.com

热门文章

  1. 托福写作1-opinions on food that are easier to prepare
  2. java中的instance_Java基础之Class类与instance关键字
  3. 百度CEO 李彦宏 简介
  4. ZigBee、WiFi、BLE大乱斗 无线通信技术究竟孰优孰劣?
  5. 宏重定义 头问题重定义解决办法
  6. c语言一个文件里重复多次定义函数,C语言里的重复定义问题的解决方案
  7. 不用修改注册表和组策略也能在 Win11 报名教师资格证
  8. windows c++编程入门
  9. mysql数据模型三要素_E-R模型的三要素为实体、属性、联系-智慧树数据库原理章节答案...
  10. 从微信小程序到QQ小程序:云开发CloudBase的一云多端实践