什么是JavaScript闭包?

46 篇文章 0 订阅
4 篇文章 0 订阅

顾名思义,所谓闭包就是**“封闭的包裹**”,意味着对外隐藏包裹内容。

为什么需要了解闭包?

对于任何一个JavaScript开发者来说,理解闭包可以看做是另一种意义上的重生。闭包是纯函数编程语言的一个特性,因为它大大简化复杂的操作,所以很容易在一些JavaScript库以及其他高级代码中找到闭包的使用。
并且这是个在JavaScript中经常谈论到的问题,被问及到什么是闭包,如果你无法回答,那么,你必定是一个初级的JavaScript开发者…

大家一定在各种论坛和博客等处查找过闭包的相关资料,但大多是描述的比较模糊,或者说即便你理解了,当天睡一觉,第二天还是会问自己什么是闭包?
那么在这里,我们将以简短易懂的方式带领大家理解闭包的概念

网上对闭包的概念主要分以下两个观点:

  • 函数套函数
  • 在函数外部获取函数内部变量

但这两个观点都不足以说服什么是闭包

你说函数套函数就是闭包?

const fun = ()=>{
    let num = 0;
    (function (){
        num += 1;
    })();
    return num;
};

const data = fun();
console.log(data); //1
const data2 = fun(); 
console.log(data2); //1
const data3 = fun();
console.log(data3); //1
const data4 = fun();
console.log(data4); //1

函数套函数就是用来解释闭包太不严谨,上面的实例代码,每次得到的data都是重新初始化的fun函数,且内部的num每次也都会重新初始化

你说在函数外获取函数内部变量就算是闭包?

function fun() {
    var innerVal = '内部变量';
    return innerVal;
}
var getInnerVal = fun ();
console.log(getInnerVal);

但是这和闭包没有丝毫关系…

闭包产生的时机
根据《你不知道的JavaScript》中的定义**“当函数作用域可以记住并访问所爱的词法作用域时,就产生了闭包,不论函数是在当前词法作用域还是内执行”**来查看这段代码:

const fun = ()=>{
    let num = 0;
    (function (){
        num += 1;
    })();
    return num;
};
fun();

虽然从概念上算是闭包,但似乎没什么作用,因为外部fun函数的执行,也立即执行了内部的匿名函数,则内部匿名函数可以记录num的状态,但由于我们每次都重新初始化fun函数内部的num变量,所以num每次的结果就是1;

所以在这里我们得出,闭包产生的时机是外部函数执行,内部函数存在的时候就形成了闭包

简单来说就是:

  • 函数内部保存的变量不随这个函数调用结束而被销毁就是闭包
  • 能够完成信息隐藏,并进而应用于需要状态表达的某些编程泛型中的是闭包
  • 闭包是由函数和其想滚得引用环境组合而成的实体

技术上的闭包:
如何将上面那个没用的闭包变得有用呢?看下面的例子

const fun = ()=>{
    let num = 0;
    return (v)=>{
        return num += v;
    };
}

const data5 = fun(); //初始化函数fun并得到函数的匿名函数返回值(这里只初始化了一次)
console.log(data5(1)); //1    给匿名函数传参并得到累加的结果
console.log(data5(1)); //2 	由于fun函数未重新初始化,且此时num的值为1,所以累加得2
console.log(data5(1)); //3	与上面雷同

由于主函数的返回值是一个匿名函数,所以我们通过return的方式每次操作那个匿名函数,而这个匿名函数则记录了num的状态,所以就形成了闭包

是不是只有这种return的方式才能形成闭包?
这是一个误解,我们使用其他例子来实现闭包

const obj = new Object();
const fun = ()=>{
    let num = 0;
    obj.method = (v)=>{
        return num += v;
    };
};
fun();
console.log(obj.method(1)); //1
console.log(obj.method(1)); //2
console.log(obj.method(1)); //3
console.log(obj.method(1)); //4

由外部Object对象中的方法来记录函数内部的状态变化

let objFun;
const fun = ()=>{
    let num = 0;
    objFun = (v)=>{
        return num += v;
    };
};
fun();
console.log(objFun(1)); //1
console.log(objFun(1)); //2
console.log(objFun(1)); //3
console.log(objFun(1)); //4

由函数外部的objFun转换为函数记录函数内部变化状态

const arr = [];
const fun = ()=>{
    let num = 0;
    arr.push((v)=>{
        return num += v;
    }); 
};
fun();
const data = arr[0](1);
console.log(data);
const data2 = arr[0](1);
console.log(data2);

由外部数组元素中的函数来保存函数内部的状态变化

所以由此证明了return和闭包没什么关系,只要将内部函数视为可观察可操作的核心,就能够形成闭包。

什么时候需要闭包?

  • 读取函数内部的变量
  • 保持变量的值始终保持在内存中

参考文章:
https://juejin.im/post/5d61182c51882543e84f41e7
https://juejin.im/post/5b081f8d6fb9a07a9b3664b6
https://juejin.im/post/5d3e55aff265da1bc552ac5d#comment

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值