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闭包内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解JavaScript中的作用域链与闭包

    目录 作用域链 词法作用域 闭包 作用域链 首先来看看这段代码: var a = '喜羊羊'; function A(){ console.log(a); a = '美羊羊'; function B(){ console.log(a); } B(); } A(); 在这里毫无疑问结果肯定是我们想到的先打印喜羊羊,再打印美羊羊.因为作用域链嘛,如果当前层没找到,那么就去当前层的上一级找. 那么再看这道 function bar() { console.log(myName) } function

  • 一文剖析JavaScript中闭包的难点

    目录 一.作用域基本介绍 1. 全局作用域 2. 函数作用域 3. 块级作用域 二.什么是闭包 1. 闭包的基本概念 2. 闭包产生的原因 3. 闭包的表现形式 三.如何解决循环输出问题 1. 利用 IIFE 2. 使用 ES6 中的 let 3. 定时器传入第三个参数 一.作用域基本介绍 ES6之前只有全局作用域与函数作用域两种,ES6出现之后,新增了块级作用域. 1. 全局作用域 在JavaScript中,全局变量是挂载在window对象下的变量,所以在网页中的任何位置你都可以使用并且访问到

  • JavaScript闭包原理与使用介绍

    目录 1. 认识闭包 2. 变量的作用域和生命周期 2.1 变量的作用域 2.2 变量的生命周期 3. 闭包的概念及其作用 3.1 闭包的概念 3.2 闭包的应用 3.2.1 保存私有变量 3.2.2 使用闭包实现节流 1. 认识闭包 闭包有一个很经典的场景:使用 for循环给上面5个按钮绑定点击事件. <button type="button" class='button'>按钮</button> <button type="button&qu

  • 详解JavaScript中的闭包是如何产生的

    目录 闭包的产生 多个内部函数共享一个闭包对象 结尾 这次从内存管理的角度来看看,闭包是怎么产生的. 我们知道,在调用函数时,其实会产生临时的 调用栈.这些调用栈保存的是 执行上下本,并实际保存在 栈内存 中. 每执行一个函数,函数内的局部临时变量会临时保存起来.如果此时函数又调用了另一个函数,另一个函数下的局部变量也要保存下来,就这样,我们产生了栈. 当一个函数执行完后,它对应的局部临时变量就会被销毁. 局部变量保存下来,是为了保护上下文现场. 举例说明一下: function a() { c

  • Javascript闭包的作用与使用方法浅析

    目录 一.变量的作用域 二.如何从外部读取局部变量 js 闭包(closure)是Javascript语言的一个难点,也是它的特色. 闭包的作用:通过一系方法,将函数内部的变量(局部变量)转化为全局变量. 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. 一.变量的作用域 Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. var n =999; function f1(){ alert(n); } f1();

  • JavaScript闭包和范围实例详解

    本文实例分析了JavaScript闭包和范围.分享给大家供大家参考,具体如下: 1. if (!("a" in window)) { var a = 1; } alert(a); [分析]代码含义:如果window不包含属性a,就声明一个变量a并赋值为1 ①JS引擎会先扫描所有的变量声明 ②所有的全局变量都是window的属性 ③变量声明和赋值一起用时,Js引擎会自动将它分成两部分:变量声明提前,变量赋值没有(不将赋值提前是因为他有可能影响代码执行出不可预期的结果) 所以代码执行顺序等

  • 轻松学习Javascript闭包

    闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 当function里嵌套function时,内部的function可以访问外部function里的变量. function foo(x) { var tmp = 3; function bar(y) { alert(x + y + (++tmp)); } bar(10); } foo(2) 不管执行多少次,都会alert 16,因为bar能访问foo的参数x,也能访问foo的变量tmp. 但

  • JavaScript知识点总结(十六)之Javascript闭包(Closure)代码详解

    闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现.很早就接触过闭包这个概念了,但是一直糊里糊涂的,没有能够弄明白JavaScript的闭包到底是什么,有什么用,今天在网上看到了一篇讲JavaScript闭包的文章(原文链接),讲得非常好,这下算是彻底明白了JavaScript的闭包到底是个神马东东以及闭包的用途了,在此写出来和大家分享一下,希望不理解JavaScript闭包的朋友们看了之后能够理解闭包!以下内容大部分是来自原文,我在原文的基础

  • javascript 闭包详解

    看了一下网上闭包的概念及文章,对于这个问题,自己做一个梳理吧. 问:闭包是什么? 答:闭包是指在 JavaScript 中,内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后. 这个是我自身第一次碰到闭包的问题 <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"/> <title>闭包循环问题</title> <

  • javascript闭包(Closure)用法实例简析

    本文实例讲述了javascript闭包(Closure)用法.分享给大家供大家参考,具体如下: closure被翻译成"闭包",感觉这东西被包装的太学术化.下面参考书本和网上资源简单探讨一下(理解不当之处务请留意). 1.什么是闭包 官方的回答:所谓"闭包",指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. 看了上面的定义,如果你不是高手,我坚信你会和我一样愤怒的质问:这tmd是人话吗? 要理解闭包,还是代码

  • Javascript 闭包详解及实例代码

    Javascript 闭包 闭包,是 Javascript 比较重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,很难从定义去理解它.因此,本文不会对闭包的概念进行大篇幅描述,直接上干货,让你分分钟学会闭包! 1 闭包,一睹为快 在接触一个新技术的时候,我首先会做的一件事就是找它的 demo code.对于我们来说,看代码比自然语言更能理解一个事物的本质.其实,闭包无处不在,比如:jQuery.zepto的核心代码都包含在一个大的闭包中,所以

  • 浅谈JS闭包中的循环绑定处理程序

    前几天工作中写前端js代码时,遇到了遍历元素给它添加单击事件.就是这个问题让我整整调了一个下午.最后还是下班回家,上网查资料才知道怎么解决的. (PS:之前也在<jQuery基础教程>第四版中看过讲循环绑定处理程序的内容,当时估计也没怎么用心看,所以没记起来.) 大神要是知道这类情况,可以关掉窗口,写这些主要是给像我一样的小白看的.谢谢! 先贴上错误的例子让大家看看.(例子里面用到jQuery,请导入jQuery库) 复制代码 代码如下: <!DOCTYPE html PUBLIC &q

  • javascript闭包功能与用法实例分析

    本文实例讲述了javascript闭包功能与用法.分享给大家供大家参考,具体如下: 理解闭包 闭包这个东西,确实是很麻烦.之前我自己的理解也是有一点误差,所以今天将文章修改修改,争取将自己的理解进一步准确化. 闭包说得通熟易懂一点,就是指有权访问另一个函数作用域的变量的函数.创建闭包的常见方式,就是在一个函数内部创建另外一个函数,并返回. 我们这里举一个例子来说明,首先我们在函数f1内部定义一个函数f2. function f1(){ var n=999; function f2(){ aler

  • javascript闭包的理解

    1.首先我们要知道变量作用域链 变量的作用域分两种:全局变量和局部变量.没有定义到任何函数中的变量为全局变量,在函数中定义的变量为局部变量,注意在函数内部定义变量时一定要使用var关键字,不带var关键字的变量为全局变量. javascript中每一段代码都有与之关联的作用域链,这个作用域链是一个对象列表或者链表,定义了这段代码"作用域"中的变量.顶层代码的作用域由全局变量组成:不包含嵌套的函数的作用域链有两个对象:一个是定义的函数参数和局部变量的对象,一个是全局变量对象:而嵌套函数的

随机推荐