JavaScript闭包中难点深入分析

 更新时间:2022年11月08日 16:03:03   作者:亦世凡华、  
闭包是js的一个难点也是它的一个特色,是我们必须掌握的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闭包中难点深入分析的文章就介绍到这了,更多相关JS闭包内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • three.js简单实现类似七圣召唤的掷骰子

    three.js简单实现类似七圣召唤的掷骰子

    这篇文章主要为大家介绍了three.js简单实现类似七圣召唤的掷骰子示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • 深入了解Javascript的事件循环机制

    深入了解Javascript的事件循环机制

    单线程的同步等待极大影响效率,任务不得不一个一个等待执行,对于网页应用是无法接受的。所以Javascript使用事件循环机制来解决异步任务的问题。本文就来讲讲Javascript的事件循环机制,希望对你有所帮助
    2022-09-09
  • 微信小程序日历弹窗选择器代码实例

    微信小程序日历弹窗选择器代码实例

    这篇文章主要介绍了微信小程序日历弹窗选择器,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-05-05
  • JS字典Dictionary类定义与用法示例

    JS字典Dictionary类定义与用法示例

    这篇文章主要介绍了JS字典Dictionary类定义与用法,结合实例形式分析了javascript字典Dictionary的定义、添加、移除、统计等相关操作技巧,需要的朋友可以参考下
    2019-02-02
  • 小程序多图列表实现性能优化的方法步骤

    小程序多图列表实现性能优化的方法步骤

    这篇文章主要介绍了小程序多图列表实现性能优化的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-05-05
  • JS实现数组浅拷贝和深拷贝的方法

    JS实现数组浅拷贝和深拷贝的方法

    浅拷贝创建一个新的对象,来接受重新复制或引用的对象值,如果对象属性是基本的数据类型,复制的就是基本类型的值给新对象,这篇文章主要介绍了js实现数组浅拷贝和深拷贝,需要的朋友可以参考下
    2024-01-01
  • 正则表达式判断是否存在中文和全角字符和判断包含中文字符串长度

    正则表达式判断是否存在中文和全角字符和判断包含中文字符串长度

    对于一些更安全的容错严重,需要用到
    2008-09-09
  • Javascript操作select控件代码实例

    Javascript操作select控件代码实例

    这篇文章主要介绍了Javascript操作select控件代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • 一文熟练掌握JavaScript的switch用法

    一文熟练掌握JavaScript的switch用法

    在JavaScript中switch语句是一种用于多条件分支的控制语句,下面这篇文章主要给大家介绍了关于如果通过一文熟练掌握JavaScript的switch用法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-01-01
  • 详解单页面路由工程使用微信分享及二次分享解决方案

    详解单页面路由工程使用微信分享及二次分享解决方案

    这篇文章主要介绍了详解单页面路由工程使用微信分享及二次分享解决方案,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-02-02

最新评论