首页 > 其他分享 >闭包详解(转)

闭包详解(转)

时间:2023-05-01 16:05:52浏览次数:31  
标签:闭包 外层 函数 作用域 详解 内存 执行


闭包:是指有权访问另外一个函数作用域中的变量的函数。创建闭包的常见方式就是在一个函数内部创建另外一个函数。 

在javascript中没有块级作用域,一般为了给某个函数申明一些只有该函数才能使用的局部变量时,我们就会用到闭包,这样我们可以很大程度上减少全局作用域中的变量,净化全局作用域。

使用闭包有如上的好处,当然这样的好处是需要付出代价的,代价就是内存的占用。

如何理解上面的那句话呢? 

每个函数的执行,都会创建一个与该函数相关的函数执行环境,或者说是函数执行上下文。这个执行上下文中有一个属性 scope chain(作用域链指针),这个指针指向一个作用域链结构,作用域链中的指针又都指向各个作用域对应的活动对象。正常情况,一个函数在调用开始执行时创建这个函数执行上下文及相应的作用域链,在函数执行结束后释放函数执行上下文及相应作用域链所占的空间。

 

比如:

//声明函数
function test(){
    var str = "hello world";
    console.log(str);
}
 
//调用函数
test();

 

在调用函数的时候会在内存中生成如下图的结构:



但是闭包的情况就有点特殊了,由于闭包函数可以访问外层函数中的变量,所以外层函数在执行结束后,其作用域活动对象并不会被释放(注意,外层函数执行结束后执行环境和对应的作用域链就会被销毁),而是被闭包函数的作用域链所引用,直到闭包函数被销毁后,外层函数的作用域活动对象才会被销毁。这也正是闭包要占用内存的原因。

 

所以使用闭包有好处,也有坏处,滥用闭包会造成内存的大量消耗。

 

使用闭包还有其他的副作用,可以说是bug,也可以说不是,相对不同的业务可能就会有不同的看法。

 

这个副作用是 闭包函数只能取到外层函数变量的最终值。

 

测试代码如下:(这里使用了jquery对象)

 

/*闭包缺陷*/
(function($){
    var result = new Array(),
    i = 0;
    for(;i<10;i++){
        result[i] = function(){
            return i;
        };
    }
    $.RES1 = result;
})(jQuery);
// 执行数组中的函数
$.RES1[0]();

 

上面的代码先通过匿名函数表达式开辟了一块私有作用域,这个匿名函数就是我们上面所说的外层函数,该外层函数有一个参数$,同时还定义了变量result 和 I , 通过for循环给数组result赋值一个匿名函数,这个匿名函数就是闭包,他访问了外层函数的变量I , 理论上数组result[i]() 会返回相应的数组下标值,实际情况却不如所愿。

如上代码 $.RES1[0]() 的执行结果是10. 

 

为什么会这样呢,因为i的最终值就是10.

 

下面我们通过下图来详细说明下,上面的那段代码执行时在内存中到底发生了什么:

 



 

那么这个副作用有没有办法可以修复呢?当然可以!

 

我们可以通过下面的代码来达到我们的预期。

 

/*修复闭包缺陷*/
(function($){
    var result = new Array(),
    i = 0;
    for(;i<10;i++){
        result[i] = function(num){
            return function(){
                return num;
            }
        }(i);
    }
    $.RES2 = result;
})(jQuery);
//调用闭包函数
console.log($.RES2[0]());

 

上面的代码又在内存中发生了什么?我们同样用下面的一幅图来详细解释。看懂了上面的图,我们也就不难理解下面的图。



只要看懂上面的三张图,我们也就可以深入的理解清楚javascript中闭包的原理,以及闭包的好处和弊端,在我们的代码中合理的使用闭包,达到代码的整洁和高效。


http://www.gbtags.com/gb/share/4160.htm


标签:闭包,外层,函数,作用域,详解,内存,执行
From: https://blog.51cto.com/yxkong/6238933

相关文章

  • PreferenceActivity详解
    为了引入这个概率首先从需求说起即:现有某Activity专门用于手机属性设置那么应该如何做呢?根据已学知识很快一个念头闪过即:Activity+Preference组合前者用于界面构建后者用于设置数据存放 其实这是正确的但是这会比较繁琐因为每个设置选项......
  • Android中Context详解 ---- 你所不…
    大家好,             Interfacetoglobalinformationaboutanapplicationenvironment.ThisisanabstractclasswhoseimplementationisprovidedbytheAndroidsystem.Itallowsaccesstoapplication-specificresourcesandclasses,aswellasup-c......
  • C++11完美转发及实现方法详解
    C++11标准为C++引入右值引用语法的同时,还解决了一个C++98/03标准长期存在的短板,即使用简单的方式即可在函数模板中实现参数的完美转发。那么,什么是完美转发?它为什么是C++98/03标准存在的一个短板?C++11标准又是如何为C++弥补这一短板的?别急,本节将就这些问题给读者做一一......
  • HTTPS原理详解
    HTTPS(全称:HypertextTransferProtocoloverSecureSocketLayer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容请看SSL。它是一个URIscheme(抽象标识符体系),句法类同http:体系。用于安全的HTTP数据传输。https:URL......
  • Java 网络编程 —— Socket 详解
    构造Socket在【客户端/服务端】的通信模式中,客户端需要主动构造与服务器连接的Socket,构造方法有以下几种重载形式:Socket()Socket(InetAddressaddress,intport)throwsUnknownHostException,IOExceptionSocket(InetAddressaddress,intport,InetAddresslocalAddr,in......
  • javaScript基础之 --- 作用域和闭包
    本文是我学习《你所不知道的javaScript上卷》的读书笔记的整理。更多详细内容,请微信搜索“前端爱好者“,戳我查看。作用域和闭包作用域是什么javaScript工作原理中的角色引擎-从头到尾负责整个javascript程序的编译及执行过程编译器-负责语法分析及代码生成......
  • 【模板方法设计模式详解】C/Java/JS/Go/Python/TS不同语言实现
    简介模板方法模式(TemplateMethodPattern)也叫模板模式,是一种行为型模式。它定义了一个抽象公开类,包含基本的算法骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变算法的结构,只是重定义该算法的某些特定步骤。不同的子类以不同的方式实现这些抽象方法,从而对剩余的逻辑有......
  • 《asyncio 系列》3. 详解 Socket(阻塞、非阻塞),以及和 asyncio 的搭配
    楔子在前面两篇文章中,我们介绍了协程、任务和事件循环,研究了如何同时运行长耗时的操作,并探索了一些可以优化此操作的asyncioAPI。然而,到目前为止,我们只是用asyncio.sleep函数模拟了长时间的操作。由于我们想要构建的不仅是演示应用程序,因此我们将使用一些真实世界的阻塞操作......
  • 《asyncio 系列》2. 详解 asyncio 的协程、任务、future,以及事件循环
    楔子上一篇文章我们深入讨论了并发性,探讨了如何同时使用进程和线程实现并发,还探索了如何利用非阻塞IO和事件循环来实现只使用一个线程的并发性。本篇文章将介绍在asyncio中使用单线程并发模型编写程序的基础知识,使用本文中的技术,你将能执行长时间运行的操作,如Web请求、数据......
  • Linux设备驱动开发详解
    Linux内核系列文章Linux内核设计与实现深入理解Linux内核Linux设备驱动程序Linux设备驱动开发详解文章目录Linux内核系列文章前言一、待续前言  本文主要用来摘录《Linux设备驱动开发详解第四版》一书中学习知识点,本书基于Linux4.0版本,源代码摘录基于Linux4.15.18......