闭包是JavaScript最重要的特性之一,也是全栈/前端/JS面试的考点。

那闭包究竟该如何理解呢?

如果不爱看文字,喜欢看视频。那本文配套讲解视频已发送到B站上供大家参考学习。

如果觉得有所收获,可以给点个赞支持一下!

地址在这:

javascript闭包讲解视频

闭包函数的判断和作用

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。

那如何判断函数是一个闭包呢?接下来我会配合一些具体的例子来对闭包问题做讲解。

首先问下大家,这个G函数是否是一个闭包呢?

const F = function A(){
return function B(){
return function C(){
return function D(){
var a = 1;
return a++
}
}
}
}
const G = F()()();
for(var i=0;i<10;i++){
console.log(G())
}

一看就是不是对吧,在这里面的G函数一看就是D函数,只不过长得比较怪而已。

如果是闭包函数那应该长成这样

const F = function A(){
var a = 1;
return function B(){
return function C(){
return function D(){
return a++
}
}
}
}
const G = F()()();
for(var i=0;i<10;i++){
console.log(G())
}

运行效果如下:

主要区别是这个变量a的声明位置。如果a是在A中声明的,那G就构成了闭包。也就是在G的作用域内,会形成一个名为closure作用域的子域。

那接下来第二个问题来了,这个a存在内存中的哪个位置呢?

在MDN中对JavaScript的定义是这样的

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

好家伙,看起来就很迷。

当定义形式难以理解的时候,我们需要语义,这也说明了一件事,我们需要调试器!

进入调试器后,一切就都明朗了起来。

我们清楚地看到,当脚本运行到 D的内部时,这个Scope也就是作用域里面包含了,Local作用域,Closure作用域和Script以及Global作用域。

Local不用说了,肯定就是函数外的对象,在这里应该是window对象。

那Closure自然就是闭包作用域了。

我们依次运行时,可以清晰地看到,closure作用域内的a在不断增加。

那第三个问题来了。

const F = function A(){
var a = 1;
return function B(){
return function C(){
return function D(){
var a = 2;
return a++
}
}
}
}
const G = F()()();
for(var i=0;i<10;i++){
console.log(G())
}

这里的G是闭包函数吗?

答案肯定不是,因为G已经能在D中找到 a变量了,那就不需要A再提供给他了,因此我们在调试器中也看不到Closure了。

我们在这里可以看到,根本没有了之前的Closure了。

现在第四个问题来了,这个程序的运行结果是什么?

const F = function A(){
var a = 1;
return function B(){
return function C(){
var a = 2;
return function D(){
return a++
}
}
}
}
const G = F()()();
for(var i=0;i<10;i++){
console.log(G())
}

这个是从2开始打印的,而非从1开始打印。

看到这,大家应该对闭包的优先级有认识,闭包也是离得越近优先级越高。

现在第五个问题来了,这个程序中,G的scope作用域里存在几个闭包?


const F = function A(){
var b = 1;
return function B(){
var c = 3;
return function C(){
var a = 2;
return function D(){
b,c
return a++
}
}
}
}
const G = F()()();
for(var i=0;i<10;i++){
console.log(G())
}

答案是3个,为什么?这里有两个角度可以解释

  1. bca在D中都没有定义,之鞥能从A,B,C中找到abc,所以这里存在三个闭包。
  2. 直接看调试器就知道啦



在调试器中我们能清楚地看到,这里有三个闭包。不解释!

闭包函数的示例

1.计数功能

在闭包函数的应用中,有很多,这里举个最常见的计数器的例子。


<html>
<head></head>
<body>
<script>
var A = (function B(){
return function C(){
var b = 0;
return function D(){
debugger
return ++b;
}
}
})() var E = A();
var F = A();
</script> <button onclick="console.log('E='+ E())">E++</button>
<button onclick="console.warn('F='+ F())">F++</button>
</body> </html>

打开后运行效果如下:



点击E++和F++后的效果

在上面的例子中我们发现,我可以用一个类似面向对象的方法,去实现计数功能。

2.setTimeout

原生的setTimeout传递的第一个函数不能带参数,通过闭包可以实现传参效果。

function func1(a) {
function func2() {
console.log(a);
}
return func2;
}
var fun = func(1);
setTimeout(fun,1000);//一秒之后打印出1

3.回调

定义行为,然后把它关联到某个用户事件上(点击或者按键)。代码通常会作为一个回调(事件触发时调用的函数)绑定到事件。

比如下面这段代码:当点击数字时,字体也会变成相应的大小。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试</title>
</head>
<body>
<a href="#" id="size-12">12</a>
<a href="#" id="size-20">20</a>
<a href="#" id="size-30">30</a> <script type="text/javascript">
function changeSize(size){
return function(){
document.body.style.fontSize = size + 'px';
};
} var size12 = changeSize(12);
var size14 = changeSize(20);
var size16 = changeSize(30); document.getElementById('size-12').onclick = size12;
document.getElementById('size-20').onclick = size14;
document.getElementById('size-30').onclick = size16; </script>
</body>
</html>

4.函数防抖

在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。

实现的关键就在于setTimeOut这个函数,由于还需要一个变量来保存计时,考虑维护全局纯净,可以借助闭包来实现。

如下代码所示:

/*
* fn [function] 需要防抖的函数
* delay [number] 毫秒,防抖期限值
*/
function debounce(fn,delay){
let timer = null //借助闭包
return function() {
if(timer){
clearTimeout(timer) //进入该分支语句,说明当前正在一个计时过程中,并且又触发了相同事件。所以要取消当前的计时,重新开始计时
timer = setTimeOut(fn,delay)
}else{
timer = setTimeOut(fn,delay) // 进入该分支说明当前并没有在计时,那么就开始一个计时
}
}
}

总之闭包的用处很多,而且很广泛。

希望这篇文章可以对大家能有所帮助!

[JavaScript闭包]Javascript闭包的判别,作用和示例的更多相关文章

  1. 5.Javascript闭包得实现原理和作用

    闭包的实现原理和作用 1.闭包的概念:指有权访问另一个函数作用域中的变量的函数,一般情况就是在一个函数中包含另一个函数. 2.闭包的作用:访问函数内部变量.保持函数在环境中一直存在,不会被垃圾回收机制 ...

  2. 前端知识体系:JavaScript基础-作用域和闭包-闭包的实现原理和作用以及堆栈溢出和内存泄漏原理和相应解决办法

    闭包的实现原理和作用 闭包: 有权访问另一个函数作用域中的变量的函数. 创建闭包的常见方式就是,在一个函数中创建另一个函数. 闭包的作用: 访问函数内部变量.保持函数在环境中一直存在,不会被垃圾回收机 ...

  3. javascript中的闭包

    闭包一直是javascript中的难点,也比较不容易被初学者所掌握,"官方"的解释是:所谓"闭包",指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是 ...

  4. JavaScript葵花宝典之闭包

    闭包,写过JS脚本的人对这个词一定不陌生,都说闭包是JS中最奇幻的一个知识点,  虽然在工作中,项目里经常都会用到~  但是是不是你已经真正的对它足够的了解~~ 又或者是你代码中出现的闭包,并不是你刻 ...

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

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

  6. 【原】理解javascript中的闭包

    闭包在javascript来说是比较重要的概念,平时工作中也是用的比较多的一项技术.下来对其进行一个小小的总结 什么是闭包? 官方说法: 闭包是指有权访问另一个函数作用域中的变量的函数.创建闭包的常见 ...

  7. 深入理解javascript原型和闭包(1)——一切都是对象

    “一切都是对象”这句话的重点在于如何去理解“对象”这个概念. ——当然,也不是所有的都是对象,值类型就不是对象. 首先咱们还是先看看javascript中一个常用的函数——typeof().typeo ...

  8. 深入理解javascript原型和闭包(18)——补充:上下文环境和作用域的关系

    本系列用了大量的篇幅讲解了上下文环境和作用域,有些人反映这两个是一回儿事.本文就用一个小例子来说明一下,作用域和上下文环境绝对不是一回事儿. 再说明之前,咱们先用简单的语言来概括一下这两个的区别. 0 ...

  9. javascript中的闭包、模块与模块加载

    一.前言 闭包是基于词法作用域(  和动态作用域对应,词法作用域是由你写代码时,将变量写在哪里来决定的,因此当词法分析器处理代码时,会保持作用)书写代码时所产生的自然结果,甚至不需要为了利用闭包而有意 ...

随机推荐

  1. 通宵修复BUG的思考

    HYH.LXJ昨晚通宵修复11月版需求的bug,因为代码提到测试环境后,阻碍了一个分行进行验收测试,业务人员直接把问题反馈给了上级领导,压力下来,项目组就把问题重视起来. 对于通宵加班这件事,应该点赞 ...

  2. Fillder抓包配置

    Faillder设置,完成以下设置后重启Fillder Fillder工具配置 设置端口 端口设置 (根据公司限制使用范围内的端口) 设置是否远程连接 勾选Decrypt HTTPS traffic ...

  3. jmeter简单的压测案例——访问百度并发5,持续请求15

    好记性不如烂笔头,还是记记吧. jmeter压测的指标有很多,先从简单的入手,下一章节再讲解jmeter组件的作用. 需求是:访问百度的压测指标是:10s内并发5,持续15次请求.那么需要设置以下几点 ...

  4. java 请求第三方接口 GET\POST 实现方法

    (1)GET方法 /** * 根据高德地图api获取位置信息 * @return * */ public static String getMapAddInfo(String httpurl) { H ...

  5. MyBatis的缓存玩法

    重要概念 SqlSession:代表和数据库的一次会话,提供了操作数据库的方法. MappedStatement:代表要发往数据执行的命令,可以理解为SQL的抽象表示. Executor:和数据库交互 ...

  6. 【DP】Educational DP Contest

    这份 dp 题单的最后几题好难 orz. 前面的题比较简单,所以我会选取一些题来讲,其它的直接看代码理解吧 qwq. 传送门: https://atcoder.jp/contests/dp 全部 AC ...

  7. 题解 「SDOI2017」硬币游戏

    题目传送门 Description 周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利. 大家纷纷觉得这个游戏非常符合同学们的特色,但只是扔硬币实在是太单调了. 同学们觉得要加强 ...

  8. 消息队列那么多,为什么建议深入了解下RabbitMQ?

    你为啥要在项目中选择xxx消息中间件? 提起消息队列,也许你的脑海里会不自觉地蹦出好多概念:JMS.Kafka.RocketMQ.AMQP.RabbitMQ.ActiveMQ.Pulsar.Redis ...

  9. IP包头结构

    版本号(Version): 长度4比特.标识目前采用的IP协议的版本号.一般的值为0100(IPv4),0110(IPv6) IP包头长度(Header Length): 长度4比特.这个字段的作用是 ...

  10. 手摸手教你用 yapi-to-typescript生成Yapi的TypeScript数据类型

    一 背景 现代社会比较重视效率,本着这个思想宗旨,能用工具自动高效做的事情,就不要低质量的勤奋.yapi-to-typescript就是一款自动生成接口请求与响应的typescript数据类型定义的工 ...