【JavaScript】一文了解JS的闭包

简介: 一文了解JS的闭包

🙉初识闭包

闭包可谓是JS的一大难点也是面试中常见的问题之一,今天开始梳理一下闭包的知识,请诸君品鉴。

🍇什么是闭包

闭包是嵌套的内部函数;内部函数包含被引用变量(函数)的对象。闭包存在于嵌套的内部函数中,例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来。当然如何直观的查看闭包可以通过chrome来查看,这里有个坑需要反馈一下,新版的chrome需要先调用fun2()才允许debugger,这样才能显示闭包。

<script>functionfn1(){
vara=2;
functionfn2(){//执行函数定义就会产生闭包(不用调用内部函数)console.log(a);
        }
//新版的chrome需要返回一下内部函数才会显示闭包returnfn2()
    }
fn1()
</script>

图片.png


🍈如何产生闭包

当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包

<script>// 将函数作为另一个函数的返回值functionfn1(){
vara=2;
functionfn2(){//a++;
console.log(a);
        }
returnfn2//将一个内部函数作为一个外部函数的返回值返回    }
varf=fn1()
//整个过程产生了一个闭包,主要看你产生了几个内部函数对象,调用了几次外部函数//闭包的特点就是函数内部的变量会一直存在于内存中,不会立即释放。f()//3   这里的f()是调用了内部函数f()//4</script>

图片.png

<script>// 将函数作为实参传递给另一个函数调用functionshowDelay(msg,time){
//setTimeout 的第一个参数是函数,符合闭包的规则setTimeout(function(){
alert(msg)
        },time)
    }
showDelay('张三',2000)
</script>

图片.png

🍉产生闭包条件

函数嵌套内部函数引用了外部函数的数据(变量/函数)

🍊闭包的作用

使用函数内部的变量在函数执行完毕后,仍然存活在内存中(延长了局部变量的生命周期);让函数外部可以操作(读写)到函数内部的数据(变量/函数)。

🍋闭包的生命周期

产生:在嵌套的内部函数定义执行完时就产生了(不是在调用),死亡:在嵌套的内部函数称为垃圾对象时就死亡了。

<script>functionfn1 () {
//此时闭包就已经产生了(函数提升,内部函数对象已经创建了)vara=2;
functionfn2 () {//a++;
console.log(a);
        }
returnfn2//将一个内部函数作为一个外部函数的返回值返回    }
varf=fn1()
f()//3   f()//4f=null//闭包死亡(包含闭包的函数对象成为垃圾对象)</script>

🍌闭包的应用

定义JS模块(具有特定功能的js文件),将所有的数据和功能都封装在一个函数的内部(私有的),只向外暴露一个包含n个方法的对象和函数;模块的使用者只需要通过模块暴露的对象调用方法来实现对应的功能。

//myModule.js 文件functionmyModule(){
// 私有数据varmsg='My Module'functionshowUpper(){
console.log('showUpper'+msg.toUpperCase());
    }
functionshowLow(){
console.log('showLow'+msg.toLowerCase());
    }
//向外暴露对象(给外部使用的方法)return {
showUpper:showUpper,
showLow:showLow    }
}
//index.html文件<scriptsrc="./myModule.js"></script><script>varmodule=myModule()
module.showUpper()
module.showLow()
</script>

图片.png

我们也可以通过匿名函数来实现闭包,这样能很便捷的调用闭包里面的属性,虽然会达到我们想要的效果,但是可能会造成全局的变量名污染,建议使用第一种。

//myModule2.js文件(function(){
// 私有数据varmsg='My Module'// 操作数据的函数functionshowUpper(){
console.log('showUpper'+msg.toUpperCase());
    }
functionshowLow(){
console.log('showLow'+msg.toLowerCase());
    }
//向外暴露对象(给外部使用的方法)window.myModule2= {
showUpper:showUpper,
showLow:showLow    }
})()
//index.js文件<scriptsrc="./myModule2.js"></script><script>myModule2.showUpper()
myModule2.showLow()
</script

图片.png

🍍闭包的缺点及解决方法

在我们使用闭包过程中,函数执行完后,函数内部的局部变量没有释放,占用内存时间会变长,容易造成内存泄漏,所以在日常开发中,尽量避免闭包的出现,或者要对局部变量及时释放。

<script>functionfn1(){
vararr=newArray[100000]
functionfn2(){
console.log(arr.length);
        }
returnfn2    }
varf=fn1()
f()
//不用闭包或者回收闭包f=null//让内部函数成为垃圾对象 --> 回收闭包</script>

内存溢出:一种程序运行出现的错误,当程序运行需要的内存超过了剩余的内存时,就会抛出内存溢出的错误。

<script>varobj= {}
for(vari=0;i<10000;i++){
obj[i]=newArray(1000000)
console.log('------');
    }
</script>

图片.png

内存泄漏:占用的内存没有及时释放,内存泄漏积累多了就容易导致内存溢出。常见的内存泄漏:意外的全局变量、没有及时清理的计时器或回调函数、闭包。

<script>//意外的全局变量functionfn(){
a=10;
console.log(a);
    }
// 调用函数虽然能打印a,但是a并没有被释放掉。一不注意就设置了一个全局变量fn()
//没有及时清理计时器或回调函数varintervalId=setInterval(function(){ //启动循环定时器后不清理console.log('--------');
    },2000)
// clearInterval(intervalId)//闭包functionfn1(){
vara=2//闭包 a 并没有被释放掉functionfn2(){
console.log(++a)
        }
returnfn2    }
varf=fn1()
f()
// f = null 不执行这条语句,a的值一直在</script>

🥭闭包案例

<script>// 案例一:varname="this is Window"varobject= {
name:"this is Object",
getName:function(){
returnfunction(){
returnthis.name            }
        }
    }
//闭包的this只能是全局,若在当前作用域中定义了this,就直接使用定义的this,若没定义,则需要一层层向外找,直到全局为止//本题是没有闭包的alert(object.getName()())//this is Window// 案例二:varname1="this is Window"varobject1= {
name1:"this is Object",
getName:function(){
//定义的that形成了闭包,内部函数引用了外部函数的变量,而this指向的是object,所以返回的是object中的name1varthat=this;
returnfunction(){
returnthat.name1            }
        }
    }
alert(object1.getName()())// this is Object</script>
<script>//没有使用闭包的话,数据是没有保留的,所以n传递给o之后,下次运算o值还是上次的值不会发生改变functionfun(n,o){
console.log(o);
return{
fun:function(m){
returnfun(m,n)
            }
        }
    }
//在执行fun(0)之后,n被之前的n=0,一直被调用vara=fun(0); //闭包里面的n传入了0a.fun(1); 
a.fun(2);
a.fun(3)//undefined,0,0,0//链式执行会导致n的改变,n是前面函数执行的形参varb=fun(0).fun(1).fun(2).fun(3)//undefined,0,1,2//c.fun(2)、c.fun(3)都调用了fun(1)留下的闭包nvarc=fun(0).fun(1); 
c.fun(2); 
c.fun(3)//undefined,0,1,1</script>


相关文章
|
10月前
|
资源调度 JavaScript 前端开发
Day.js极简轻易快速2kB的JavaScript库-替代Moment.js
dayjs是一个极简快速2kB的JavaScript库,可以为浏览器处理解析、验证、操作和显示日期和时间,它的设计目标是提供一个简单、快速且功能强大的日期处理工具,同时保持极小的体积(仅 2KB 左右)。
611 24
|
前端开发 JavaScript Java
JavaScript闭包深入剖析:性能剖析与优化技巧
JavaScript 闭包是强大而灵活的特性,广泛应用于数据封装、函数柯里化和事件处理等场景。闭包通过保存外部作用域的变量,实现了私有变量和方法的创建,提升了代码的安全性和可维护性。然而,闭包也可能带来性能问题,如内存泄漏和执行效率下降。为优化闭包性能,建议采取以下策略:及时解除对不再使用的闭包变量的引用,减少闭包的创建次数,使用 WeakMap 管理弱引用,以及优化闭包结构以减少作用域链查找的开销。在实际开发中,无论是 Web 前端还是 Node.js 后端,这些优化措施都能显著提升程序的性能和稳定性。
318 70
|
11月前
|
存储 JavaScript 前端开发
|
自然语言处理 JavaScript 前端开发
当面试官再问我JS闭包时,我能答出来的都在这里了。
闭包(Closure)是前端面试中的高频考点,广泛应用于函数式编程中。它不仅指函数内部定义的函数,还涉及内存管理、作用域链和垃圾回收机制。闭包可以让函数访问其外部作用域的变量,但也可能引发内存泄漏等问题。通过合理使用闭包,可以实现模块化、高阶函数和回调函数等应用场景。然而,滥用闭包可能导致代码复杂度增加、调试困难以及潜在的性能问题。为了避免这些问题,开发时应谨慎处理闭包,避免不必要的嵌套,并及时清理不再使用的变量和监听器。
523 16
当面试官再问我JS闭包时,我能答出来的都在这里了。
|
JavaScript 前端开发
JavaWeb JavaScript ③ JS的流程控制和函数
通过本文的详细介绍,您可以深入理解JavaScript的流程控制和函数的使用,进而编写出高效、可维护的代码。
279 32
|
JavaScript 前端开发 算法
JavaScript 中通过Array.sort() 实现多字段排序、排序稳定性、随机排序洗牌算法、优化排序性能,JS中排序算法的使用详解(附实际应用代码)
Array.sort() 是一个功能强大的方法,通过自定义的比较函数,可以处理各种复杂的排序逻辑。无论是简单的数字排序,还是多字段、嵌套对象、分组排序等高级应用,Array.sort() 都能胜任。同时,通过性能优化技巧(如映射排序)和结合其他数组方法(如 reduce),Array.sort() 可以用来实现高效的数据处理逻辑。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
缓存 自然语言处理 JavaScript
JavaScript中闭包详解+举例,闭包的各种实践场景:高级技巧与实用指南
闭包是JavaScript中不可或缺的部分,它不仅可以增强代码的可维护性,还能在模块化、回调处理等场景中发挥巨大作用。然而,闭包的强大也意味着需要谨慎使用,避免潜在的性能问题和内存泄漏。通过对闭包原理的深入理解以及在实际项目中的灵活应用,你将能够更加高效地编写出简洁且功能强大的代码。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
数据采集 JavaScript 前端开发
JavaScript中通过array.filter()实现数组的数据筛选、数据清洗和链式调用,JS中数组过滤器的使用详解(附实际应用代码)
用array.filter()来实现数据筛选、数据清洗和链式调用,相对于for循环更加清晰,语义化强,能显著提升代码的可读性和可维护性。博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
JavaScript 前端开发
【JavaScript】——JS基础入门常见操作(大量举例)
JS引入方式,JS基础语法,JS增删查改,JS函数,JS对象