当前位置: 首页 > news >正文

【JavaScript】一文了解JS的闭包

🍈作者简介:大家好,我是亦世凡华、渴望知识储备自己的一名在校大学生

🍇个人主页:亦世凡华、的博客

🍓系列专栏:JavaScript专栏

🥝推荐一款模拟面试刷题神器🔥:点击跳转进入网

目录

🙉初识闭包

🍇什么是闭包

🍈如何产生闭包

🍉产生闭包条件

🍊闭包的作用

🍋闭包的生命周期

🍌闭包的应用

🍍闭包的缺点及解决方法

🥭闭包案例


🙉初识闭包

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

🍇什么是闭包

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

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

🍈如何产生闭包

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

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

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

🍉产生闭包条件

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

🍊闭包的作用

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

🍋闭包的生命周期

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

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

🍌闭包的应用

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

//myModule.js 文件
function myModule(){
    // 私有数据
    var msg = 'My Module'
    function showUpper(){
        console.log('showUpper' +msg.toUpperCase());
    }
    function showLow(){
        console.log('showLow' +msg.toLowerCase());
    }
    //向外暴露对象(给外部使用的方法)
    return {
        showUpper:showUpper,
        showLow:showLow
    }
}

//index.html文件
<script src="./myModule.js"></script>
<script>
    var module = myModule()
    module.showUpper()
    module.showLow()
</script>

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

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

//index.js文件
<script src="./myModule2.js"></script>
<script>
    myModule2.showUpper()
    myModule2.showLow()
</script

🍍闭包的缺点及解决方法

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

<script>
    function fn1(){
        var arr = new Array[100000]
        function fn2(){
            console.log(arr.length);
        }
        return fn2
    }
    var f = fn1()
    f()
    //不用闭包或者回收闭包
    f = null//让内部函数成为垃圾对象 --> 回收闭包
</script>

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

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

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

<script>
    //意外的全局变量
    function fn(){
        a = 10;
        console.log(a);
    }
    // 调用函数虽然能打印a,但是a并没有被释放掉。一不注意就设置了一个全局变量
    fn()

    //没有及时清理计时器或回调函数
    var intervalId = setInterval(function(){ //启动循环定时器后不清理
        console.log('--------');
    },2000)
    // clearInterval(intervalId)

    //闭包
    function fn1(){
        var a = 2 //闭包 a 并没有被释放掉
        function fn2(){
            console.log(++a)
        }
        return fn2
    }
    var f = fn1()
    f()
    // f = null 不执行这条语句,a的值一直在
</script>

🥭闭包案例

<script>
    // 案例一:
    var name = "this is Window"
    var object = {
        name:"this is Object",
        getName:function(){
            return function(){
                return this.name
            }
        }
    }
    //闭包的this只能是全局,若在当前作用域中定义了this,就直接使用定义的this,若没定义,则需要一层层向外找,直到全局为止
    //本题是没有闭包的
    alert(object.getName()())//this is Window

    // 案例二:
    var name1 = "this is Window"
    var object1 = {
        name1:"this is Object",
        getName:function(){
            //定义的that形成了闭包,内部函数引用了外部函数的变量,而this指向的是object,所以返回的是object中的name1
            var that = this;
            return function(){
                return that.name1
            }
        }
    }
    alert(object1.getName()())// this is Object
</script>
<script>
    //没有使用闭包的话,数据是没有保留的,所以n传递给o之后,下次运算o值还是上次的值不会发生改变
    function fun(n,o){
        console.log(o);
        return{
            fun:function(m){
                return fun(m,n)
            }
        }
    }
    //在执行fun(0)之后,n被之前的n=0,一直被调用
    var a = fun(0); //闭包里面的n传入了0
    a.fun(1); 
    a.fun(2);
    a.fun(3)//undefined,0,0,0
     
    //链式执行会导致n的改变,n是前面函数执行的形参
    var b = fun(0).fun(1).fun(2).fun(3)//undefined,0,1,2

    //c.fun(2)、c.fun(3)都调用了fun(1)留下的闭包n
    var c = fun(0).fun(1); 
    c.fun(2); 
    c.fun(3)//undefined,0,1,1
</script>

🍃JavaScript的学习还是要以多练习为主,想要练习JavaScript的朋友,推荐可以去牛客网看一看,链接:牛客网 里面的IT题库内容很丰富,属于国内做的很好的了,最重要的是里面的资源是免费的,是课程+刷题+面经+求职+讨论区分享,一站式求职学习网站,感兴趣的可以去看看。

相关文章:

  • 2022前端vue面试题
  • docker相关试题
  • python 之名称空间与作用域
  • python之面向过程编程思想与匿名函数及其应用
  • docker 之Dockerfile
  • python之logging 模块(简洁版)
  • Docker 搭建 Redis Cluster 集群环境
  • Docker 容器编排利器 Docker Compose
  • Docker Swarm 集群环境搭建及弹性服务部署
  • Docker Compose 搭建 Redis Cluster 集群环境
  • 计算机初识
  • 计算机硬件五大单元
  • 计算机硬件组成详解
  • 计算机硬盘接口及操作系统
  • 计算机核心概念之进程、线程、进程池、进程三态、同步、异步、并发、并行、串行...
  • [原]深入对比数据科学工具箱:Python和R 非结构化数据的结构化
  • css布局,左右固定中间自适应实现
  • Markdown 语法简单说明
  • MYSQL如何对数据进行自动化升级--以如果某数据表存在并且某字段不存在时则执行更新操作为例...
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • Rancher如何对接Ceph-RBD块存储
  • rc-form之最单纯情况
  • Sass Day-01
  • TypeScript实现数据结构(一)栈,队列,链表
  • WinRAR存在严重的安全漏洞影响5亿用户
  • 从@property说起(二)当我们写下@property (nonatomic, weak) id obj时,我们究竟写了什么...
  • 前端自动化解决方案
  • 使用前端开发工具包WijmoJS - 创建自定义DropDownTree控件(包含源代码)
  • 网页视频流m3u8/ts视频下载
  • 学习笔记DL002:AI、机器学习、表示学习、深度学习,第一次大衰退
  • 异步
  • #define,static,const,三种常量的区别
  • #我与Java虚拟机的故事#连载07:我放弃了对JVM的进一步学习
  • #中的引用型是什么意识_Java中四种引用有什么区别以及应用场景
  • #中国IT界的第一本漂流日记 传递IT正能量# 【分享得“IT漂友”勋章】
  • (附源码)php新闻发布平台 毕业设计 141646
  • (附源码)python房屋租赁管理系统 毕业设计 745613
  • (太强大了) - Linux 性能监控、测试、优化工具
  • (原創) 人會胖會瘦,都是自我要求的結果 (日記)
  • (转)拼包函数及网络封包的异常处理(含代码)
  • .NET C#版本和.NET版本以及VS版本的对应关系
  • .NET CLR Hosting 简介
  • .NET 命令行参数包含应用程序路径吗?
  • .NET 设计模式初探
  • .NET使用存储过程实现对数据库的增删改查
  • .NET序列化 serializable,反序列化
  • .net之微信企业号开发(一) 所使用的环境与工具以及准备工作
  • [20150321]索引空块的问题.txt
  • [AIR] NativeExtension在IOS下的开发实例 --- IOS项目的创建 (一)
  • [BZOJ] 2006: [NOI2010]超级钢琴
  • [Codeforces] number theory (R1600) Part.11
  • [Flex][问题笔记]TextArea滚动条问题
  • [Hadoop in China 2011] 蒋建平:探秘基于Hadoop的华为共有云
  • [hdu 3652] B-number
  • [IE9] GPU硬件加速到底是实用创新还是噱头