js深入理解闭包(附上代码)
本篇文章主要介绍了js的闭包,闭包算是js里面比较不容易理解的点,现在整理出来分享给大家,有需要的可以了解一下。
闭包算是js里面比较不容易理解的点,尤其是对于没有编程基础的人来说。
其实闭包要注意的就那么几条,如果你都明白了那么征服它并不是什么难事儿。下面就让我们来谈一谈闭包的一些基本原理。
闭包的概念
一个闭包就是一个函数和被创建的函数中的作用域对象的组合。(作用域对象下面会说)
通俗一点的就是 “ 只要一个函数中嵌套了一个或多个函数,那么我们就可以称它们构成了闭包。 ”
类似这样:
function A() { var i = 5; return function() { console.log('i = '+i); } } var a = A(); a(); // i = 5
闭包的原理
1、外部函数的局部变量若会被闭包函数调用就不会在外部函数执行完毕之后立即被回收。
我们知道,不管什么语言,操作系统都会存在一个垃圾回收机制,将多余分配的空间回收掉以便减小内存。而一个函数的生命周期的是从调用它开始的,在函数调用完毕的时候函数内部的局部变量等都会被回收机制回收。
我们拿上述例子来说,当我们的外部函数A调用完毕时,A中的局部变量i按理说就会被操作系统回收而不存在,但是当我们用了闭包结果就不是那样了,i并不会被回收。试想,如果i被回收了那么返回的函数里面岂不是就是打印undefined了?
i为什么没有被回收?
在javascript执行一个函数的时候都会创建一个作用域对象,将函数中的局部变量(函数的形参也是局部变量)保存进去,伴随着那些传入函数的变量一起被初始化。
所以当调用A的时候就创建了一个作用域对象,我们姑且称之为Aa,那么这个Aa应该是这样的: Aa { i: 5; }; 在A函数返回一个函数之后,A执行完毕。Aa对象本应该被回收,但是由于返回的函数使用了Aa的属性i,所以返回的函数保存了一个指向Aa的引用,所以Aa不会被回收。
所以理解作用域对象,就能理解为什么函数的局部变量在遇到闭包的时候不会在函数调用完毕时立即被回收了。
再来个例子:
function A(age) { var name = 'wind'; var sayHello = function() { console.log('hello, '+name+', you are '+age+' years old!'); }; return sayHello; } var wind = A(20); wind(); // hello, wind, you are 20 years old!
你能说出的它的作用域对象Ww是什么吗?
Ww{ age: 20; name: 'wind'; };
2、每调用一次外部函数就产生一个新的闭包,以前的闭包依旧存在且互不影响。
3、同一个闭包会保留上一次的状态,当它被再次调用时会在上一次的基础上进行。
每调用一次外部函数产生的作用域对象都不一样,你可以这样想,上面的例子,你每次传入的参数age不一样,所以就每次生成的对象不一样。
每调用一次外部函数那么就会生成一个新的作用域对象。
function A() { var num = 42; return function() { console.log(num++); } } var a = A(); a(); // 42 a(); // 43 var b = A(); // 重新调用A(),形成新闭包 b(); // 42
这个代码让我们发现了两个事情,一、当我们连续调用两次a();,num会在原基础上自加。说明同一个闭包会保留上一次的状态,当它被再次调用时会在上一次的基础上进行。 二、我们的b();的结果为42,说明它是一个新的闭包,并且不受其他闭包的影响。
我们可以这样想,就好比我们吹肥皂泡一样,我每次吹一下(调用外部函数),就会产生一个新的肥皂泡(闭包),多个肥皂泡可以同时存在且两个肥皂泡之间不会相互影响。
4、在外部函数中存在的多个函数 “ 同生共死 ”
以下三个函数被同时声明并且都可以对作用域对象的属性(局部变量)进行访问与操作。
var fun1, fun2, fun3; function A() { var num = 42; fun1 = function() { console.log(num); } fun2 = function() { num++; } fun3 = function() { num--; } } A(); fun1(); // 42 fun2(); fun2(); fun1(); // 44 fun3(); fun1(); //43 var old = fun1; A(); fun1(); // 42 old(); // 43 上一个闭包的fun1()
由于函数不能有多个返回值,所以我用了全局变量。我们再次可以看出在我们第二次调用A()时产生了一个新的闭包。
当闭包遇到循环变量
当我们说到闭包就不得不说当闭包遇到循环变量这一种情况,看如下代码:
function buildArr(arr) { var result = []; for (var i = 0; i < arr.length; i++) { var item = 'item' + i; result.push( function() {console.log(item + ' ' + arr[i])} ); } return result; } var fnlist = buildArr([1,2,3]); fnlist[0](); // item2 undefined fnlist[1](); // item2 undefined fnlist[2](); // item2 undefined
怎么会这样呢?我们预想的三个输出应该是 item0 1, item1 2, item2 3。为什么结果却是返回的result数组里面存储了三个 item2 undefined ?
原来当闭包遇到循环变量时都是循环结束之后统一保存变量值,拿我们上面的例子来说,i是循环变量,当循环全部结束的时候i正好是i++之后的3,而arr[3]是没有值的,所以为undefined,有人会疑惑:为什么item的值是item2,难道不应该是item3吗?注意,在最后一次循环的时候也就是i = 2的时候,item的值为item2,当i++,i = 3循环条件不满足循环结束,此时的item的值已经定下来了,所以此时的arr[i]为arr[3],而item为item2。这样能理解吗?如果我们将代码改成这样那就说得通了:
function buildArr(arr) { var result = []; for (var i = 0; i < arr.length; i++) { result.push( function() {console.log('item' + i + ' ' + arr[i])} ); } return result; } var fnlist = buildArr([1,2,3]); fnlist[1](); // item3 undefined
那么问题来了,如何改正呢?且看代码:
function buildArr(arr) { var result = []; for (var i = 0; i < arr.length; i++) { result.push( (function(n) { return function() { var item = 'item' + n; console.log(item + ' ' + arr[n]); } })(i)); } return result; } var fnlist = buildArr([1,2,3]); fnlist[0](); // item0 1 fnlist[1](); // item1 2 fnlist[2](); // item2 3
我们可以用一个自执行函数将i绑定,这样i的每一个状态都会被存储,答案就和我们预期的一样了。
所以以后在使用闭包的时候遇到循环变量我们要习惯性的想到用自执行函数来绑定它。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
以上是js深入理解闭包(附上代码)的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

有的用户在安装设备的时候遇到了错误,提示错误代码28,其实这主要是由于驱动程序的原因,我们只要解决win7驱动程序代码28的问题就可以了,下面就一起来看一下应该怎么来操作吧。win7驱动程序代码28怎么办:首先,我们需要点击屏幕左下角的开始菜单。接着,在弹出的菜单中找到并点击“控制面板”选项。这个选项通常位于菜单的底部或者附近。点击后,系统会自动打开控制面板界面。在控制面板中,我们可以进行各种系统设置和管理操作。这是怀旧大扫除关卡中的第一步,希望对大家有所帮助。然后,我们需要继续操作,进入系统和

蓝屏代码0x0000001怎么办蓝屏错误是电脑系统或硬件出现问题时的一种警告机制,代码0x0000001通常表示出现了硬件或驱动程序故障。当用户在使用电脑时突然遇到蓝屏错误,可能会感到惊慌和无措。幸运的是,大多数蓝屏错误都可以通过一些简单的步骤进行排除和处理。本文将为读者介绍一些解决蓝屏错误代码0x0000001的方法。首先,当遇到蓝屏错误时,我们可以尝试重

win10系统是一款非常优秀的高智能系统强大的智能可以为用户们带来最好的使用体验,一般正常的情况下用户们的win10系统电脑都不会出现任何的问题!但是在优秀的电脑也难免会出现各种故障最近一直有小伙伴们反应自己的win10系统遇到了频繁蓝屏的问题!今天小编就为大家带来了win10电脑频繁蓝屏不同代码的解决办法让我们一起来看一看吧。电脑频繁蓝屏而且每次代码不一样的解决办法:造成各种故障代码的原因以及解决建议1、0×000000116故障原因:应该是显卡驱动不兼容。解决建议:建议更换厂商原带驱动。2、

终止代码0xc000007b在使用电脑时,有时会遇到各种各样的问题和错误代码。其中,终止代码最为令人困扰,尤其是终止代码0xc000007b。这个代码表示某个应用程序无法正常启动,给用户带来了不便。首先,我们来了解一下终止代码0xc000007b的含义。这个代码是Windows操作系统的错误代码,通常发生在32位应用程序尝试在64位操作系统上运行时。它表示应

蓝屏是我们在系统使用的时候经常会碰到的问题,根据错误代码的不同,会有很多中不一样的原因和解决方法。例如我们在使用时遇到stop:0x0000007f的问题,可能是硬件或软件错误,下面就跟着小编一起来看看解决方法吧。0x000000c5蓝屏代码原因:答:内存、CPU、显卡突然超频,或软件运行错误。解决方法一:1、在开机时候不断按F8进入,选择安全模式,回车进入。2、进入到安全模式后,按win+r打开运行窗口,输入cmd,回车。3、在命令提示窗口,输入“chkdsk/f/r”,回车,然后按y键。4、

如果您需要远程编程任何设备,这篇文章会给您带来帮助。我们将分享编程任何设备的顶级GE通用远程代码。通用电气的遥控器是什么?GEUniversalRemote是一款遥控器,可用于控制多个设备,如智能电视、LG、Vizio、索尼、蓝光、DVD、DVR、Roku、AppleTV、流媒体播放器等。GEUniversal遥控器有各种型号,具有不同的功能和功能。GEUniversalRemote最多可以控制四台设备。顶级通用遥控器代码,可在任何设备上编程GE遥控器配备一组代码,使其能够与不同设备相配合。您可

0x000000d1蓝屏代码是什么意思近年来,随着计算机的普及和网络的快速发展,操作系统的稳定性和安全性问题也日益凸显。一个常见的问题是蓝屏错误,代码0x000000d1是其中之一。蓝屏错误,或称为“蓝屏死机”,是当计算机遇到严重系统故障时发生的一种情况。当系统无法从错误中恢复时,Windows操作系统会显示一个蓝色的屏幕,并在屏幕上显示错误代码。这些错误代

快速上手Python绘图:画出冰墩墩的代码示例Python是一种简单易学且功能强大的编程语言,通过使用Python的绘图库,我们可以轻松地实现各种绘图需求。在本篇文章中,我们将使用Python的绘图库matplotlib来画出冰墩墩的简单图形。冰墩墩是一只拥有可爱形象的熊猫,非常受小朋友们的喜爱。首先,我们需要安装matplotlib库。你可以通过在终端运行
