javascript中的闭包这一篇就够了

前端技术优选 今天

以下文章来源于程序员成长指北 ,作者koala

程序员成长指北专注 Node.js 技术栈分享,从 前端 到 Node.js 再到 后端数据库,祝您成为优秀的高级 Node.js 全栈工程师。一个有趣的且乐于分享的人。座右铭:今天未完成的,明天更不会完成。

什么是闭包

维基百科中的概念

  • 在计算机科学中,闭包(也称词法闭包或函数闭包)是指一个函数或函数的引用,与一个引用环境绑定在一起,这个引用环境是一个存储该函数每个非局部变量(也叫自由变量)的表。

  • 闭包,不同于一般的函数,它允许一个函数在立即词法作用域外调用时,仍可访问非本地变量

学术上

  • 闭包是指在 JavaScript 中,内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回return掉(寿命终结)了之后。

个人理解

  • 闭包是在函数里面定义一个函数,该函数可以是匿名函数,该子函数能够读写父函数的局部变量。

闭包的常见案例分析

案例分析是从浅入深希望大家都看完!

  • 案例1---基本介绍:

function A(){    var localVal=10;    return localVal;}

A();//输出30

function A(){    var localVal=10;    return function(){         console.log(localVal);         return localVal;    }}var func=A();func();//输出10

两段代码,在第二段代码中,函数A内的匿名函数可以访问到函数A中的局部变量这就是闭包的基本使用。

  • 案例2---前端实现点击事件

!function(){    var localData="localData here";    document.addEventListener('click',function(){    console.log(localData);    });}();

前端原始点击事件操作也用到了闭包来访问外部的局部变量。

  • 案例3---ajax请求

!function(){    var localData="localData here";    var url="http://www.baidu.com";    $.ajax({      url:url,      success:function(){          //do sth...          console.log(localData);      }    })}();

在ajax请求的方法中也用到了闭包,访问外部的局部变量。

  • 案例4---for循环案例

var arrays = [];

for (var i=0; i<3; i++) {    arrays.push(function() {        console.log('>>> ' + i); //all are 3    });}

上面的这段代码,刚看了代码一定会以为陆续打印出1,2,3,实际输出的是3,3,3,出现这种情况的原因是匿名函数保存的是引用,当for循环结束的时候,i已经变成3了,所以打印的时候变成3。出现这种情况的解决办法是利用闭包解决问题。

for (var i=0; i<3; i++) {    (function(n) {        tasks.push(function() {            console.log('>>> ' + n);        });    })(i);}

闭包里的匿名函数,读取变量的顺序,先读取本地变量,再读取父函数的局部变量,如果找不到到全局里面搜索,i作为局部变量存到闭包里面,所以调整后的代码可以能正常打印1,2,3。

闭包与内存泄漏

  • javascript回收后内存的方式:

javascript的主要通过计数器方式回收内存,假设有a,b,c三个对象,当a引用b的时候,那么b的引用计算器增加1(通俗的说用到那个对象哪个对象引用计算器增加1),同时b引用c的时候,c引用计数器增加1,当a被释放的时候,b的引用计数器减少1,变成0的时候这个对象被释放,c计数器变成0,被释放,但是当遇到b和c之间互相引用的时候,无法通过计数器方式释放内存。

  • 闭包可以导致上面所说b和c互相引用无法释放内存 第一个案例的代码拿过来分析:

function A(){    var localVal=10;    return function(){         console.log(localVal);         return localVal;    }}var func=A();func();//输出10

当A函数结束的时候,想要释放,发现它的localVal变量被匿名函数引用,所有A函数无法释放,导致内存泄漏。但是也有好处,闭包正是可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留。

说明:闭包不代表一定会带来内存泄漏,良好的使用闭包是不会造成内存泄漏的。

闭包的应用

  • 封装

var person = function(){    //变量作用域为函数内部,外部无法访问    var name = "default";

    return {       getName : function(){           return name;       },       setName : function(newName){           name = newName;       }    }}();

print(person.name);//直接访问,结果为undefinedprint(person.getName());person.setName("kaola");print(person.getName());

得到结果如下:

undefined  default  kaola
  • 实例中的for循环另一种形式

doucument.body.innerHTML="<div id=div1>aaa</div>"+"<div id=div2>bbb</div>"+"<div id=div3>ccc</div>";for(var i=1;i<4;i++){    !function(i){        document.getElementById('div'+i);        addEventListener('click',function(){           alert(i);//1,2,3        });    }}
  • 结果缓存

var CachedSearchBox = (function(){    var cache = {},       count = [];    return {       attachSearchBox : function(dsid){           if(dsid in cache){//如果结果在缓存中              return cache[dsid];//直接返回缓存中的对象           }           var fsb = new uikit.webctrl.SearchBox(dsid);//新建           cache[dsid] = fsb;//更新缓存           if(count.length > 100){//保正缓存的大小<=100              delete cache[count.shift()];           }           return fsb;       },

       clearSearchBox : function(dsid){           if(dsid in cache){              cache[dsid].clearSelection();           }       }    };})();

CachedSearchBox.attachSearchBox("input");

说明:开发中会碰到很多情况,设想我们有一个处理过程很耗时的函数对象,每次调用都会花费很长时间,那么我们就需要将计算出来的值存储起来,当调用这个函数的时候,首先在缓存中查找,如果找不到,则进行计算,然后更新缓存并返回值,如果找到了,直接返回查找到的值即可。闭包正是可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留。

面试题分析

闭包测试题: 求输出结果

function fun(n,o){    console.log(o);    return {        fun:function(m){//[2]            return fun(m,n);//[1]        }    }}

var a=fun(0);a.fun(1);a.fun(2);a.fun(3);var b=fun(0).fun(1).fun(2).fun(3);var c=fun(0).fun(1);c.fun(2);c.fun(3);

由于分析内容比较多,大家可直接参考这篇文章 https://cnodejs.org/topic/567ed16eaacb6923221de48f

分析内容说明,在看这篇文章的时候,注意两点可能会看的更明白:

  • JS的词法作用域,JS变量作用域存在于函数体中即函数体,并且变量的作用域是在函数定义声明的时候就是确定的,而非在函数运行时。

  • 在JS中调用函数的时候,如果用一个参数的方法调用两个参数的方法,这时候只是第二个参数未定义,代码不会报错停止运行,正常流程往下走,像面试题中仍然会返回一个对象。

总结

  1. 闭包其实是在函数内部定义一个函数。

  2. 闭包在使用的时候不会释放外部的引用,闭包函数内部的值会得到保留。

  3. 闭包里面的匿名函数,读取变量的顺序,先读取本地变量,再读取父函数的局部变量。

  4. 对于闭包外部无法引用它内部的变量,因此在函数内部创建的变量执行完后会立刻释放资源,不污染全局对象。

  5. 闭包使用的时候要考虑到内存泄漏,因为不释放外部引用,但是合理的使用闭包是内存使用不是内存泄漏。

(0)

相关推荐

  • 闭包理解

    闭包 刚学过的闭包,分享一下闭包的理解 1.什么是闭包? 闭包就是一个函数,也可以说闭包是一个引用关系,可以理解为一个作用域可以访问另一个函数的局部变量. 代码: function fn() { va ...

  • JavaScript

    简介 JavaScript是一门动态弱类型的解释型编程语言.是可插入 HTML 页面的编程代码,插入 HTML 页面后,可由所有的现代浏览器执行.从而增强页面动态效果,实现页面与用户之间的实时动态的交 ...

  • 菜鸟记136-EXCEL中的批注问题,看这一篇就够了

    注:本图由专业摄影师甜甜溪水授权使用 关键词:EXCEL2016:批注:插入.编辑.替换批注:操作难度** 有的时候,我们需要在EXCEL单元格中进行批注,以方便阅读者更好了解其中的含义,比如像这样: ...

  • 初中物理中的图像题,有这一篇就够了!

    在物理学习中,常采用数学中的函数图像,将某些物理量之间的关系表示出来,因此图像实际上反映了物理变化过程的特点以及物理量之间的变化关系.将物理过程和物理量之间的关系在图像上呈现出来,可使物理过程形象.直 ...

  • 中声区要注意哪些问题?看这篇就够了~

    在歌唱中,中声区是承上启下的音区,在学歌唱的过程中可以说至关重要.中声区歌唱的音调和说话的音调相当接近,但不能等同说话,需要用歌唱的方法去应对.首先要保持声音有良好的气息支持,想到用气就有气息上来,胸 ...

  • 《史记》中最精妙的一篇文章,贡献三句经典名言,影响世人两千年!

    在<史记>中,要说哪篇文章最为精妙,那<陈涉世家>必定榜上有名.这篇文章记述了农民陈胜吴广首举义旗,推翻秦朝强暴统治的历史. 陈胜,字涉,虽是一个农民,却有着不同反响的人生,在 ...

  • 练腹肌,看这一篇就够了!高、中、低难度动作集齐了

    文章简要: 1.腹肌的构成 2.简单动作 3.中等动作 4.困难动作 1.腹肌的构成 腹肌是人体结缔组织中的重要部分,腹肌又被分为四个部分: 腹直肌.腹内斜肌.腹外斜肌.腹横肌. 腹直肌:位于腹前壁正 ...

  • BNP/NT-proBNP在心衰中的应用,这篇说的够清楚!

    BNP和NT-proBNP是目前在心衰诊疗中应用最广泛的生物标志物.理想的标志物应该具备以下特征:敏感性高,特异性强,标志物水平的变化可反映病情的变化,采用标志物指导的治疗策略更具客观性等.那么有哪些 ...

  • 绘本故事中的主角们—兔子篇

    温顺可爱的兔子,每个小朋友可能都想养几只,它也是绘本故事里出镜率较高的动物之一,咱们今天就找几只兔子,看看它们有什么样的稀奇事. 兔子是好奇先生 <兔子先生有个大工程>,讲了一个关于友谊和 ...

  • 双色球第2021056期,上期手稿中6+0,本篇文章告诉你怎么组合

    双色球第2021055期开奖号码03 11 12 16 22 28+10:最后的手稿是命中6+0的,很多人说不会看不会组合,本篇文章就来告诉你怎么看手写的草稿怎么组合: 当然你要知道该怎么去组合起来! ...

  • 正方形中的45度解答篇上

    正方形中的45度解答篇上