【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>


相关文章
|
1月前
|
自然语言处理 JavaScript 前端开发
深入理解JavaScript中的闭包:原理与实战
【10月更文挑战第12天】深入理解JavaScript中的闭包:原理与实战
|
23天前
|
JavaScript 前端开发
js 闭包的优点和缺点
【10月更文挑战第27天】JavaScript闭包是一把双刃剑,在合理使用的情况下,它可以带来很多好处,如实现数据封装、记忆功能和模块化等;但如果不注意其缺点,如内存泄漏、变量共享和性能开销等问题,可能会导致代码出现难以调试的错误和性能问题。因此,在使用闭包时,需要谨慎权衡其优缺点,根据具体的应用场景合理地运用闭包。
106 58
|
23天前
|
缓存 JavaScript 前端开发
js 闭包
【10月更文挑战第27天】JavaScript闭包是一种强大的特性,它可以用于实现数据隐藏、记忆和缓存等功能,但在使用时也需要注意内存泄漏和变量共享等问题,以确保代码的质量和性能。
36 7
|
25天前
|
自然语言处理 JavaScript 前端开发
JavaScript闭包:解锁编程潜能,释放你的创造力
【10月更文挑战第25天】本文深入探讨了JavaScript中的闭包,包括其基本概念、创建方法和实践应用。闭包允许函数访问其定义时的作用域链,常用于数据封装、函数柯里化和模块化编程。文章还提供了闭包的最佳实践,帮助读者更好地理解和使用这一强大特性。
15 2
|
7天前
|
JSON JavaScript 前端开发
使用JavaScript和Node.js构建简单的RESTful API
使用JavaScript和Node.js构建简单的RESTful API
|
1月前
|
设计模式 JavaScript 前端开发
探索JavaScript中的闭包:从基础概念到实际应用
在本文中,我们将深入探讨JavaScript中的一个重要概念——闭包。闭包是一种强大的编程工具,它允许函数记住并访问其所在作用域的变量,即使该函数在其作用域之外被调用。通过详细解析闭包的定义、创建方法以及实际应用场景,本文旨在帮助读者不仅理解闭包的理论概念,还能在实际开发中灵活运用这一技巧。
|
1月前
|
缓存 JavaScript 前端开发
深入了解JavaScript的闭包:概念与应用
【10月更文挑战第8天】深入了解JavaScript的闭包:概念与应用
|
1月前
|
人工智能 JavaScript 前端开发
使用Node.js模拟执行JavaScript
使用Node.js模拟执行JavaScript
|
1月前
|
Web App开发 JavaScript 前端开发
Node.js:JavaScript世界的全能工具
Node.js:JavaScript世界的全能工具
|
1月前
|
自然语言处理 JavaScript 前端开发
深入理解JavaScript中的闭包:原理、应用与代码演示
【10月更文挑战第12天】深入理解JavaScript中的闭包:原理、应用与代码演示