当前位置:首页 > 日记 > 正文

JavaScript中闭包的详解

JavaScript中闭包的详解

闭包是什么

在 JavaScript 中,闭包是一个让人很难弄懂的概念。ECMAScript 中给闭包的定义是:闭包,指的是词法表示包括不被计算的变量的函数,也就是说,函数可以使用函数之外定义的变量。

是不是看完这个定义感觉更加懵逼了?别急,我们来分析一下。

  • 闭包是一个函数
  • 闭包可以使用在它外面定义的变量
  • 闭包存在定义该变量的作用域中

好像有点清晰了,但是使用在它外面定义的变量是什么意思,我们先来看看变量作用域。

变量作用域

变量可分为全局变量和局部变量。全局变量的作用域就是全局性的,在 js 的任何地方都可以使用全局变量。在函数中使用 var 关键字声明变量,这时的变量即是局部变量,它的作用域只在声明该变量的函数内,在函数外面是访问不到该变量的。

var func = function(){  var a = 'linxin';  console.log(a);     // linxin}func();console.log(a);       // Uncaught ReferenceError: a is not defined

作用域相对比较简单,我们不多讲,来看看跟闭包关系比较大的变量生存周期。

变量生存周期

全局变量,生命周期是永久的。局部变量,当定义该变量的函数调用结束时,该变量就会被垃圾回收机制回收而销毁。再次调用该函数时又会重新定义了一个新变量。

var func = function(){  var a = 'linxin';  console.log(a);}func();

a 为局部变量,在 func 调用完之后,a 就会被销毁了。

var func = function(){  var a = 'linxin';  var func1 = function(){    a += ' a';    console.log(a);  }  return func1;}var func2 = func();func2();          // linxin afunc2();          // linxin a afunc2();          // linxin a a a

可以看出,在第一次调用完 func2 之后,func 中的变量 a 变成 'linxin a',而没有被销毁。因为此时 func1 形成了一个闭包,导致了 a 的生命周期延续了。

这下子闭包就比较明朗了。

  • 闭包是一个函数,比如上面的 func1 函数
  • 闭包使用其他函数定义的变量,使其不被销毁。比如上面 func1 调用了变量 a
  • 闭包存在定义该变量的作用域中,变量 a 存在 func 的作用域中,那么 func1 也必然存在这个作用域中。

现在可以说,满足这三个条件的就是闭包了。

下面我们通过一个简单而又经典的例子来进一步熟悉闭包。

for (var i = 0; i < 4; i++) {  setTimeout(function () {    console.log(i)  }, 0)}

我们可能会简单的以为控制台会打印出 0 1 2 3,可事实却打印出了 4 4 4 4,这又是为什么呢?我们发现,setTimeout 函数时异步的,等到函数执行时,for循环已经结束了,此时的 i 的值为 4,所以 function() { console.log(i) } 去找变量 i,只能拿到 4。

我们想起上一个例子中,闭包使 a 变量的值被保存起来了,那么这里我们也可以用闭包把 0 1 2 3 保存起来。

for (var i = 0; i < 4; i++) {  (function (i) {    setTimeout(function () {      console.log(i)    }, 0)  })(i)}

当 i=0 时,把 0 作为参数传进匿名函数中,此时 function(i){} 此匿名函数中的 i 的值为 0,等到 setTimeout 执行时顺着外层去找 i,这时就能拿到 0。如此循环,就能拿到想要的 0 1 2 3。

内存管理

在闭包中调用局部变量,会导致这个局部变量无法及时被销毁,相当于全局变量一样会一直占用着内存。如果需要回收这些变量占用的内存,可以手动将变量设置为null。

然而在使用闭包的过程中,比较容易形成 JavaScript 对象和 DOM 对象的循环引用,就有可能造成内存泄露。这是因为浏览器的垃圾回收机制中,如果两个对象之间形成了循环引用,那么它们都无法被回收。

function func() {  var test = document.getElementById('test');  test.onclick = function () {    console.log('hello world');  }}

在上面例子中,func 函数中用匿名函数创建了一个闭包。变量 test 是 JavaScript 对象,引用了 id 为 test 的 DOM 对象,DOM 对象的 onclick 属性又引用了闭包,而闭包又可以调用 test ,因而形成了循环引用,导致两个对象都无法被回收。要解决这个问题,只需要把循环引用中的变量设为 null 即可。

function func() {  var test = document.getElementById('test');  test.onclick = function () {    console.log('hello world');  }  test = null;}

如果在 func 函数中不使用匿名函数创建闭包,而是通过引用一个外部函数,也不会出现循环引用的问题。

function func() {  var test = document.getElementById('test');  test.onclick = funcTest;}function funcTest(){  console.log('hello world');}

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!

相关文章

ppt怎么实现局部区域的黑底白字效

ppt怎么实现局部区域的黑底白字效

区域,白字,黑底,局部,效果,  ppt怎么实现局部区域的黑底白字效果?PPT制作是信息时代下的产物,ppt具有较好的展示功能,能够形象生动的表达意愿和目的;下面小编来告诉你吧。ppt实现局部区域的黑底白字效果的方法:  ①启动PowerPoint2003,插…

怎么在word中给文章添加脚注在word

怎么在word中给文章添加脚注在word

步骤,方法,脚注,文章,电脑软件,  在日常办公室里,我们写文档的时候有时候要注释脚注,如果是新手的话就摸不着头脑,下面小编就教你怎么在word中给文章添加脚注。word中给文章添加脚注的步骤首先打开word文本word中给文章添加脚注的步骤图1 …

node.js实现的装饰者模式示例

node.js实现的装饰者模式示例

装饰者模式,示例,电脑软件,node,js,本文实例讲述了node.js实现的装饰者模式。分享给大家供大家参考,具体如下:装饰者模式的实现更强调类的组合而不是通过继承。这样可以增强灵活性。在node.js 中,可以通过call函数实现。call函数可以在一个对…

怎么去除wrod的背景色去除wrod的背

怎么去除wrod的背景色去除wrod的背

背景色,方法,背景颜色,教程,步骤,  word的背景色大概有三种,一种是页面背景,不管有没有字,页面都是呈现背景色;还有一种所示突出显示,为了让文字突出,给其添加了背景色;最后一种是字符底纹,就是凡是有字的地方就添加上一个底纹颜色。下面小编就…

Angularjs中的ui-bootstrap的使用

Angularjs中的ui-bootstrap的使用

使用教程,电脑软件,Angularjs,ui,bootstrap,1.新建uiBootstrap.html页面,引入依赖的js和css类库2.新建uiBootstrap.js文件,定义一个uiModule 模块,引入依赖的模块/** * Created by zhong on 2015/9/7. */var uiModule = angular.module("uiMo…

jQuery中的deferred使用方法

jQuery中的deferred使用方法

使用方法,电脑软件,jQuery,deferred,deferred简介deferred对象是jQuery的回调函数解决方案,jQuery之前的版本中异步回调这块做的不是很好,所以后期补上了该解决方案。普遍的ajax操作方式我们先来回顾一下jQuery中普通的ajax操作$.ajax({ url:…

word设置表格表头的方法Word怎样设

word设置表格表头的方法Word怎样设

设置,表头,方法,表格,技巧,  在制作表格时,经常回遇到制作表头的情况,那么应该如何制作表头呢?其实制作标题的方法很简单,接下来小编举例简单的例子告诉大家word设置表格表头的方法,希望能帮到大家。word设置表格表头的方法打开word,如果插入…

qq浏览器默认搜索引擎修改方法

qq浏览器默认搜索引擎修改方法

修改,浏览器,默认,方法,搜索引擎,  QQ浏览器安装后默认的搜索引擎并不是百度,这是因为QQ浏览器与搜狐进行了合作,不过搜狐旗下的搜狗有时候会找不到相关的内容,那么怎么把QQ浏览器的默认引擎换成百度呢?下面就跟小编来了解一下吧!qq浏览器默…

js学习心得_一个简单的动画库封装t

js学习心得_一个简单的动画库封装t

封装,动画,学习心得,简单,电脑软件,具体代码如下:~function(){ var myEffect = { Linear:function(t,b,c,d){ return c*t/d+b }, Quad: {//二次方的缓动(t^2); easeIn: function(t,b,c,d){ return c*(t/=d)*t + b; …

word2013怎么给文字添加底纹word20

word2013怎么给文字添加底纹word20

文字,方法,底纹,电脑软件,strong,  为了美化文档,我们可以适当给文档的内容添加底纹和边框,添加底纹也可以作为标记重点来使用,那么要怎么做才能给word2013的内容加底纹呢?下面小编来告诉你吧。word2013给文字添加底纹的方法方法一:  选中…

qq空间标题栏怎么弄没

qq空间标题栏怎么弄没

空间,标题栏,方法,怎么弄,电脑软件,  qq空间标题栏怎么弄没?有时看到别人的空间特别美丽,格式跟我们的也不一样,很是羡慕,那时就特别想有一个漂亮的空间。那我们今天就来设置一个漂亮的空间首页,把qq空间的标题栏隐藏起来。隐藏qq空间标题栏…

Vue2.0父组件与子组件之间的事件发

Vue2.0父组件与子组件之间的事件发

组件,发射,事件,实例代码,与子,关于vue2.0的事件发射和接收,大家都知道$dispatch和$broadcast在vue2.0已经被弃用了,取而代之的是更加方便快捷的方式,使用事件中心,组件通过它来互相通信,不管组件在哪一个层都可以通过实例化一个空Vue来实现。上…