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


相关文章
|
22天前
|
JavaScript
闭包(js的问题)
闭包(js的问题)
12 0
|
26天前
|
机器学习/深度学习 人工智能 JavaScript
js和JavaScript
js和JavaScript
21 4
|
1月前
|
设计模式 JavaScript 前端开发
js开发:请解释闭包(closure)是什么,以及它的用途。
闭包是JavaScript中的关键特性,允许函数访问并操作外部作用域的变量,常用于实现私有变量、模块化和高阶函数。私有变量示例展示了如何创建无法外部访问的计数器;模块化示例演示了封装私有变量和函数,防止全局污染;高阶函数示例则说明了如何使用闭包创建能接收或返回函数的函数。
16 2
|
1月前
|
存储 缓存 JavaScript
|
1月前
|
自然语言处理 JavaScript 前端开发
探索JavaScript中的闭包:理解其原理与实际应用
探索JavaScript中的闭包:理解其原理与实际应用
19 0
|
1天前
|
测试技术
js_防抖与节流(闭包的使用)
js_防抖与节流(闭包的使用)
7 0
|
3天前
|
JavaScript 前端开发
js开发:请解释this关键字在JavaScript中的用法。
【4月更文挑战第23天】JavaScript的this关键字根据执行环境指向不同对象:全局中指向全局对象(如window),普通函数中默认指向全局对象,作为方法调用时指向调用对象;构造函数中指向新实例,箭头函数继承所在上下文的this。可通过call、apply、bind方法显式改变this指向。
7 1
|
25天前
|
JavaScript 前端开发
JavaScript生成的随机数随机字符串JS生成的随机数随机字符串
JavaScript生成的随机数随机字符串JS生成的随机数随机字符串
14 1
|
30天前
|
JavaScript 前端开发 Java
深入剖析 JavaScript 闭包
深入探讨JavaScript闭包,了解其定义、特性、优缺点及作用。闭包是函数与其引用环境的组合,允许内层函数访问外层作用域,常驻内存可能导致内存泄露。优点包括创建私有变量,缺点则涉及内存使用。闭包在变量搜索中遵循从内到外的规则,并影响变量的作用域和生存周期。理解闭包有助于优化代码并避免性能问题。
21 1
|
1月前
|
JavaScript 前端开发
js开发:请解释什么是模块化(modularization),并说明如何在JavaScript中实现模块化。
模块化将复杂系统拆分为松散耦合的模块,提高代码可读性、可维护性、可复用性和可扩展性。JavaScript模块化历经CommonJS(Node.js中常见,使用`require()`和`module.exports`)、AMD(RequireJS,异步加载,`define()`和`require()`)和ES6 Modules(官方标准,`import`和`export`)三个阶段。打包工具如Webpack、Rollup处理兼容性问题,使模块化代码能在各种环境中运行。