在面试过程中,各位童鞋经常会被问道这样的问题:“请描述下你对闭包的理解”,或者在面试烤卷中会有关于闭包的选择、填空题。如果是前者,大可一句带过:“闭包就是一个函数有权访问另一个函数作用域中的变量”。如果是后者,那我们拿起笔的那只小爪爪可能会有一丝颤抖~~~(我对闭包真的熟悉吗?)。

PS: 面试中的闭包相关知识点总是喜欢结合Javascript作用域、声明提前、事件循环、this一起进行!

为了能让大家在再次遇到有关闭包的问题时,能做到"心不虚,手不抖,LZ跟着感觉走"。所以接下来,我要为大家表演一个"我吹闭包,如吹大乌苏"。走起!!!

开场白:请各位大声告诉我下面的代码打印了什么?为什么?

function daRio() {let name = "剑大瑞"let callMe = function(bilibili) {return bilibili + name}return callMe
}daRio()("帅哥")

先看代码,我们在上面的代码中做了哪些事情?

  • daRio中分别创建了一个变量name,一个匿名函数callMe
  • 在匿名函数中 return 一个 bilibili (callMe 参数)+ name(daRio函数作用域中的变量name)
  • 将callMe返回
  • 调用daRio,并传入参数

正戏:

创建变量时发生了什么?

当JavaScript引擎在执行代码之前会经历三个步骤:

分词/词法分析——》解析/语法分析——》代码生成

最终结果代码会转化为一组机器指令,紧接着开始执行。在上面这个过程中其实有三个不同的角色相互配合,分别是Javascript引擎、编译器、作用域。

  • Javascript负责整个Javascript代码的编译及执行

  • 编译器负责进行语法分析及代码生成

  • 作用域根据一套非常严格的家规(规则)收集并维护变量一系列的查询,确定谁有权限访问谁。

扒一扒:作用域

作用域的查询规则主要有两种工作模型,一种是词法作用域,遵循词法作用域的查询模型关注的是标识符定义的位置,比如Javascript.另一种是动态作用域,遵循动态作用域的查询模型关注的是函数从何处调用,其作用域链是基于运行时的调用栈的,比如Base、Perl。

当我们看到 let name = “剑大瑞” 时,JS引擎会分为两步执行,一步由编译器进行编译时处理,一步由引擎在运行时处理,

  • 在编译器处理时会首先询问作用域是否有一个name的变量存在,如果有,则编译器会忽略该声明,继续编译,否则作用域会创建一个新的变量,并命名为name。
  • 接着由Javascript引擎执行name = "剑大瑞"操作,在执行过程中引擎会询问作用域,当前作用域中是否有name变量存在,如果有则进行赋值操作,如果没有,引擎会继续进行查找操作。如果最终没有找到,引擎就会抛出一个异常!

在上面的代码中

  • 声明并创建daRio
  • 声明并创建name
  • 声明并创建callMe
  • 传参

都是在进行下面这个操作:

首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后在运行在时引擎会在作用域中查找该变量,如果能找到就对变量进行赋值。

捋一捋JS引擎继续查找的操作:作用域链

前面说到当引擎在询问当前作用域中是否存在变量时,如果没有找到当前变量则会继续查找,如果找到就停止,没有就抛错。其实这一过程这就是我们所说的作用域链查找。

每当我们创建一个函数时,都会生成一个函数作用域,而这个函数作用域中就会保存当前函数参数和局部变量,如果存在作用域嵌套的话还会有一个作用域链指针,指向包裹该函数的包含环境。

这个过程是通过层层嵌套的作用域链最终找到我们的目标变量,直至我们的全局环境(在浏览器中即windows)。并且作用域链直接保证了执行环境有权访问的所有变量、函数的有序访问。

通过这张图片我们可以看到在callMe函数中存在一个[[Scopes]]属性,当然我们不能通过callMe.Scopes访问到他,但是我们实实在在使用了他,这里面保存了两个对象指针一个是Closure,一个Global。当我们的Js引擎在执行过程中发现在callMe作用域中没有找到变量name,就会沿着Scopes去查找,如果通过Closure找到则停止(即使Global中还存在同一个变量name)。这就是作用域链为我们callMe函数所提供的变量访问权限!至此我们也就明白了为什么我们可以在callMe中访问到daRio中的name。

闭包

文章写到这里,我已经感觉我的台词已经用完了,闭包已经没得解释了~~~,通过上面的内容我们已经把闭包最为本质的东西扒完了。不过还是要一句话总结下闭包的原理:

其实闭包就是基于JavaScript的词法作用域,当嵌套函数在外部环境执行的过程中通过作用域链访问到包含它的函数作用域中变量所形成的一种现象。并且由于嵌套函数存在对包含函数变量引用的原因,导致外部作用域中的变量无法及时销毁,会占用一定的内。如果闭包过多,则会影响程序性能。

用途

  • 模块化
let daRio = (function() {let myAttr = {name: "剑大瑞"gender: "man",age: 18,height: 180,weChat: 185****0350}let callMe = function(bilibili) {return bilibili + myAttr.name}let introduceMe = function() {return  `Hi sweetie, my name is ${myAttr.name}, I\`m  ${myAttr.age} years old and  ${myAttr.height} , this\`s my weChat ${myAttr.weChat} `}return {callMe,introduceMe}})()daRio.callMe("帅哥") daRio.introduceMe()

通过创建函数作用域 + 利用闭包的特点,我们可以实现简单的模块化

  • 柯里化 Currying

    通过Js函数柯里化,可以实现函数参数的缓存效果。在日常的开发任务中我们回经常使用到这项技术,比如bind的实现,React中的高阶组件等等。

function youInfo(gender) {let style if(gender) {style = "小姐姐"} else {style = "小锅锅"}return function(name) {return style + name}
}
let me = youInfo(1)
console.log(me("剑大瑞")) // 打印了什么?
  • 经典面试题

    • 改造下面的代码,使之输出0 - 9,写出你能想到的所有解法。
for (var i = 0; i< 10; i++){setTimeout(() => {console.log(i);}, 1000)
}

PS:这道题涉及到JS的异步事件,如果吃透,对于理解JS的事件循环机制及异步非常有帮助哦~

  • 请写出如下代码的打印结果
 var name = 'Tom';
(function() {if (typeof name == 'undefined') {var name = 'Jack';console.log('Goodbye ' + name);} else {console.log('Hello ' + name);}
})();

PS:这道题涉及到Javascript的变量提升及函数作用域,请先分辨出它有没有产生闭包呢?为什么?可以在评论区留下答案哈

  • 下面的题目打印结果是什么,如果想输出1,2,3怎么实现?
    var data = [];for (var i = 0; i < 3; i++) {data[i] = function () {console.log(i);};}data[0]();   data[1]();data[2]();

节目的最后,给大家留个问题:函数可以通过作用域链访问到上层甚至上上层的变量,但是为什么当闭包存在时,使用this会出错呢?

关于"闭包"的这杯酒我是吹完了,各位呢?

参考文献:

  • 《Javascript权威指南》
  • 《Javascript高级程序设计》
  • 《你不知道的Javascript》上卷
  • 《MDN — 闭包》
  • 面试题引用—木易杨前端进阶每日一题

Javascript深入浅出之闭包相关推荐

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

    (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue.React.Node源码和实战.面试指导) 本周正式开始前端进阶的第二期,本周的主题是作用域闭包,今天是第8天. 本计划一共28期,每 ...

  2. JavaScript深入浅出第2课:函数是一等公民是什么意思呢?

    摘要: 听起来很炫酷的一等公民是啥? <JavaScript深入浅出>系列: JavaScript深入浅出第1课:箭头函数中的this究竟是什么鬼? JavaScript深入浅出第2课:函 ...

  3. 让你分分钟学会Javascript中的闭包

    Javascript中的闭包 前面的话: 闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它 ...

  4. 深入理解javascript原型和闭包(16)——完结

    之前一共用15篇文章,把javascript的原型和闭包. 首先,javascript本来就"不容易学".不是说它有多难,而是学习它的人,往往都是在学会了其他语言之后,又学java ...

  5. 深入理解javascript原型和闭包(17)——补this

    本文对<深入理解javascript原型和闭包(10)--this>一篇进行补充,原文链接:http://www.cnblogs.com/wangfupeng1988/p/3988422. ...

  6. 深入理解javascript原型和闭包(3)——prototype原型

    既typeof之后的另一位老朋友! prototype也是我们的老朋友,即使不了解的人,也应该都听过它的大名.如果它还是您的新朋友,我估计您也是javascript的新朋友. 在咱们的第一节(深入理解 ...

  7. [译]Javascript中的闭包(closures)

    本文翻译youtube上的up主kudvenkat的javascript tutorial播放单 源地址在此: https://www.youtube.com/watch?v=PMsVM7rjupU& ...

  8. 【javascript笔记】关于javascript中的闭包

    最开始看<javascript高级程序设计>的时候就看到了javascript中的闭包,在第七章第二节....好大概知道了,过了段时间,好了又忘了... 我们来看这本书里面关于闭包是怎么描 ...

  9. 解析面试常问题之JavaScript中的闭包概念及应用,顺便普及一下大家口中常说的内存泄漏问题

    JavaScript中的闭包是一个面试中经常被考到的问题,大家可能都对这个概念多多少少都有一些模糊的概念或者一点都不了解,那么今天就来给大家讲解一下. 公众号:前端印象 不定时有送书活动,记得关注~ ...

最新文章

  1. 在Python中实现SVM分类
  2. java 常量折叠_V8 使用“常量折叠”优化技巧,导致幂(**)运算有时候不等于 Math.pow()...
  3. 如何量化考核技术人的 KPI?
  4. java使用druid maven_SpringMVC+Spring+Mybatis整合,使用druid连接池,声明式事务,maven配置...
  5. mysql团队开发工具_最棒的10款MySQL GUI工具
  6. kerberos 下运行spark 报错 Requested user hdfs is banned
  7. java 根据类名示例化类_Java即时类| plusMillis()方法与示例
  8. Save as XPS in Office “12”
  9. elementUI select组件value值注意事项
  10. 服务器下 读取springboot application配置文件_一文读懂 Spring Boot 配置文件 application.properties !...
  11. 在vue项目中使用图片浏览组件v-viewer,支持旋转、缩放、翻转等操作
  12. matlab提取汉字拼音,中文转拼音工具
  13. C/C++打印百分号 %
  14. Java 发送QQ邮件
  15. 如何推广APP软件?
  16. [战略]Fans未来战略--第3篇--以文会友
  17. 汉诺塔问题(Tower of Hanoi)
  18. 写给程序员的编程语言科普——前言
  19. 树莓派wifi探针_树莓派重新开始|搭建博客、网盘、流媒体与离线下载服务
  20. 安装Anaconda遇到问题--NSIS Error

热门文章

  1. 精华文章置顶--CDC系列之一 :使用Dejournal Filter在InterSystems IRIS/Caché上通过Mirroring实现CDC功能...
  2. android app自动拍照6,6款手机拍照处理软件,大片生成,值得点赞收藏,不快来看一下?...
  3. python提取微信聊天语音_利用Python进行微信,QQ的语音识别!内部技术的延伸版!...
  4. 错误: Failed to install 'unknown package' from GitHub: schannel: failed to receive handshake, SSL/TL
  5. 你不知道的 Blob
  6. zigbee芯片方案和模组选型
  7. 2018年淘宝新店开业怎么引流量
  8. 24点卡牌游戏C++实现
  9. C#腾讯地图Web端定位地址搜索及手机导航
  10. 云原生数据中台:架构、方法论与实践