JavaScript 闭包详解

JavaScript 闭包详解JavaScript闭包文章目录JavaScript闭包一、为什么要闭包二、外部得以访问函数内变量三、某些变量得以常驻内存1.垃圾回收机制对闭包的处理2.结合立即执行函数来保存某些变量总结#前言##1.什么是闭包函数闭包函数是声明在另一个函数内的函数,是被嵌套在父函数内部的子函数,在《JS高级程序设计-第3版》中对闭包解释是:”闭包是指有权访问另外一个函数作用域中的变量的函数.”闭包函数可以访问[包裹其的函数]内的各种参数和变量,即便外部函数已经执行完毕.(至于为什么请看下文).一、为什么

大家好,又见面了,我是你们的朋友全栈君。

JavaScript闭包


# 前言-什么是闭包函数 闭包函数是声明在另一个函数内的函数,是被嵌套在父函数内部的子函数,在《JS高级程序设计-第3版》中对闭包解释是:”闭包是指有权访问另外一个函数作用域中的变量的函数.” 闭包函数可以访问[包裹其的函数]内的各种参数和变量,即便外部函数已经执行完毕.(至于为什么请看下文).

一、为什么要闭包

  1. 使外部得以访问函数内部的变量;
  2. 避免全局变量的使用,防止全局变量污染(匿名函数);
  3. 让某些关键变量得以常驻内存,免于被回收销毁(闭包函数);

二、让某些变量得以常驻内存

我们需要将立即执行函数与闭包结合;
我们都知道JavaScript是自带垃圾回收机制的,对于函数来说,在其执行完毕后会被垃圾回收机制回收来进行内存释放,函数内部的局部对象(各种变量之类)也会被连同销毁使内存中仅仅保存全局作用域.

1.原理

前言说到闭包函数就是一个嵌套在父函数里面并且有使用父函数变量的子函数, 闭包函数的执行必定依赖于父函数提供的数据,但要是调用闭包函数时父函数已经被销毁,闭包函数怎么执行呢?
没法执行,因为闭包函数所依赖的变量也都被销毁,总不能因为要执行闭包函数再把父函数提出来,不太合理;

所以不能就这么回收掉,但是保存整个父函数又有点离谱,所以JavaScript垃圾回收机制只会保存闭包函数在父函数中所依赖到的变量这些被保存起来的变量不会被内存回收器回收,直到确认子函数不会再被调用(子函数被删除或者失去指针)为止;

类比一下,如果你学过webpack的话,应该会知道webpack在打包一个文件的时候会把这个文件依赖的所有文件一起打包,就是为了防止使用的时候出问题,垃圾回收机制是在删减的时候留下需要的,weboack是在打包的时候加上需要的.

2.Why 立即执行函数?

我想探讨一下为什么推荐用立即执行函数来配合闭包进行变量保存…
一开始我猜为了在闭包函数保存完需要的变量后父函数能被及时回收释放内存,才采用了匿名立即执行函数来作为闭包函数的父函数.因为立即执行函数自我回调执行完成后会被立即销毁回收,用一次就释放,节约内存(但因为销毁快,外界无法引用其内部的变量)
后来看到了一个例子,作者将使用了立即执行函数的闭包和没有使用立即执行函数的闭包进行了比较,让我改变了想法:

//例1,这个例子中没有使用立即执行函数;
function createFunction() { 
   
    var Array = [];
    for( var i = 0; i<10; i++) { 
   
    //将函数赋值给每个数组元素;
        Array[i] = function() { 
   
            return i;
        };
    }
    return Array;
}

var aa = createFunction();
console.log(aa[0]()); //10
console.log(aa[1]()); //10

由于作用域链的配置机制,因为每个函数的作用域链中保存的都是createFunctions()的活动对象,所以每个函数引用的都是活动对象中的同一个变量 i。
(活动对象: 在JavaScript中,当一个函数被创建时最后一步便是活动对象推入作用域链,函数中访问一个变量时会从作用域链中搜索具有相应名字的变量,函数执行完后局部活动对象会被销毁,活动对象中包含了参数列表和arguments对象等属性. 活动对象包含变量对象所有的属性)
当createFunctions() 函数执行结束返回后,变量 i 的值就已经固定为10,而每个函数保存的变量对象里的 i 都出自createFunctions()的活动对象,每个函数拿到的 i 都是出自同一个活动对象的,都一样,所以最后不论输出哪个数组元素得到的都是10.
即说明了闭包中所保存的是整个活动对象,而不是某个具体的变量,这种机制并不是我们想要的,我们希望它能把每个变量单独保存下来,所以就有了能解决这个问题的,使用了立即执行函数的例子,即例2:

function createFunction() { 
   
    var result = new Array();
    for( var i = 0; i<10; i++) { 
   
    
        result[i] = function(num) { 
   
        //每接收一个num就会创建新的一个函数作用域;
            return function() { 
   
            //在每个作用域的内部创建并返回一个返回num的闭包函数
                return num;
            };
        }(i);
     //变量i的当前值会作为实参赋值给上面的形参num;
     
    }
    return result;
}
//在外部使用函数内变量;
var bb = createFunction();
alert(bb[0]()); //0
alert(bb[1]()); //1

闭包函数依赖到了外部立即执行函数的num,所以num会连同闭包函数被保存下来免于销毁,这样result[ ]中被赋值进去的每个函数都能返回一个自己的num,我们的目的就能达到了,完成这一目标的关键就是使用了立即执行函数.

这个闭包函数的父函数函数每接收一个num就会创建新的一个函数作用域(见例3),作用域中传入i后,变量i的当前值会作为实参赋值给上面的形参num,而在当前每个作用域的内部,又创建并返回了一个返回num的闭包函数。这样一来传入每个函数作用域中闭包函数的num就是不同的了.如此类推,被赋值进入result数组中的每个函数作用域都有一个自己num(其实是时num副本),可以返回各自不同的数值了.

for(var i = 0; i < 5; i++) { 
   
  abc(i);
};

function abc(i) { 
   
  setTimeout(function() { 
   
     console.log(i);			// 0 1 2 3 4 
  }, 200); 
}
//这里就相当于创建了5个函数作用域;

可见立即执行函数在保存变量时泛用性比普通函数强;

三、让外部得以访问函数内变量

外部访问函数内变量跟立即执行函数没什么必然关系,不使用立即执行函数也可以进行保存,上面说到的结合立即执行函数的写法只是针对某些特殊情况下无法依据需求保存变量的问题,我们不得不承认立即执行函数泛用性好一些.

在外部调用父函数即可拿到闭包函数内的变量;

四、立即执行函数

刚学到的,单独开一篇感觉也没必要,正好这里用到了就写下来吧…

//这两种写法是会报错的;
(function() { 
   
//函数体;
 })(); 

function() { 
   
//函数体;
 }();

JavaScript引擎先看到了你的”function”关键字,然后就开始以函数声明标准规范你后续的代码,最终JavaScript引擎发现你用一个小括号结束了你的函数,它觉得这是错的.
我们不能否定它判定的规则,人家认为写了function就是要声明函数,那我们就不要上来直接写function了:

var myFunction = function () { 
   
                   /* 函数体 */ 
                 }();
var myObj = { 
   
    myFunction: function () { 
   
                  /* 函数体 */ }
                }();

让JavaScript引擎先看到小括号而不是function关键字,它就会觉得你在写函数表达式,也就判定为合理了;


总结

比较重要的是闭包会造成内存泄漏.闭包会把一些东西永驻保存下来,而且前面提到的它所依赖的东西都不会被销毁,自己的局部活动对象和依赖到的活动对象都会被包含到它自己的作用域链里,所以它的体量往往是比普通函数大上老些;

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/148667.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 目标检测的目的_小目标检测问题

    目标检测的目的_小目标检测问题我们在评价一个目标检测算法的“好坏”程度的时候,往往采用的是pascalvoc2012的评价标准mAP。网上一些资料博客参差不齐,缺乏直观易懂的正确说明。希望这篇博文能够给大家一点帮助。mAP历史目标检测的mAP计算方式在2010年的voc上发生过变化,目前基本都是采用新的mAP评价标准。(我有个小疑问就是明明是2010年修改的,但是貌似现在大家都称这种计算方式为2012)所…

    2022年10月12日
  • flutter的介绍和环境搭建(超详细)

    flutter的介绍和环境搭建(超详细)

  • 如何制作bt种子文件「建议收藏」

    首先,先整一个比特彗星然后打开点击文件,点击制作torrent文件在tracker服务器这一项里添加这些服务器http://torrentzilla.org/announcehttp://kinorun.com/announce.phphttp://torrent-team.net/announce.phphttp://bt.3dmgame.com:2710/announcehttp://tracker.ali213.net:8080/announcehttp://bt.ali.

  • 阿里云图片存储_阿里云oss价格

    阿里云图片存储_阿里云oss价格oss目的:1.图片和音视频等应用的海量存储2.网页或者移动应用的静态和动态资源分离利用BGP带宽,OSS可以实现超低延时的数据直接下载。也可以配合阿里云CDN加速服务3.云端数据处理比如图片的裁剪,缩放,鉴黄等oss使用:1.http://www.aliyun.com/product/登录阿里云,开通oss图片存储(如果你还没有账号需要先注册获取AccessKey…

    2022年10月28日
  • idea2022 license server激活【2021最新】

    (idea2022 license server激活)最近有小伙伴私信我,问我这边有没有免费的intellijIdea的激活码,然后我将全栈君台教程分享给他了。激活成功之后他一直表示感谢,哈哈~IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.cn/100143.html…

  • java中的static关键字的作用?

    java中的static关键字的作用?是静态修饰符,什么叫静态修饰符呢?大家都知道,在程序中任何变量或者代码都是在编译时由系统自动分配内存来存储的,而所谓静态就是指在编译后所分配的内存会一直存在,直到程序退出内存才会释放这个空间,也就是只要程序在运行,那么这块内存就会一直存在。这样做有什么意义呢?在Java程序里面,所有的东西都是对象,而对象的抽象就是类,对于一个类而言,如果要使用他的成员,那么普通情况下必须先实例化对象后,通过对象

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号