摘要:从我年开始接触前端,知道闭包这个词,已经过去两年了。概念闭包,在高级程序设计里面是这样介绍的闭包是指有权访问另一个作用域中的变量的函数。这样形成的闭包虽然可以使外部可以访问到内部的函数,但是导致了原有的作用域链不释放,会造成内存泄漏。
从我16年开始接触前端,知道闭包这个词,已经过去两年了。这两年里,闭包这个概念我在很多地方了解过,却实在没有真的理解,久而久之,变成了一块心病。这不,趁着现在项目告一段落的时间,我又开始折腾它了,在网易云课堂上找了些资源来看。哈哈,可能是因为已经做了些项目的缘故,这次终于能理解它到底是个什么玩意儿了。
概念闭包,在《javascripts高级程序设计》里面是这样介绍的:闭包是指有权访问另一个作用域中的变量的函数。额。。这句话我以前看过很多遍,但依然不是很懂,只知道它是跟作用域有关。现在我知道了,如果这句话换成:但凡是内部的函数被保存到了外部,必定生成闭包。这样就容易理解多了不是。
我们以下面的这个代码块为例:
function a() { const num = 100; function b () { num++; console.log(num); } return b; } const demo = a(); demo(); demo();
我们先执行上述代码,看看结果是什么:
为什么是这样呢?
a()执行的结果是返回b,所以demo指的是b,则demo()指的是b();也就是说原先在a里面的b,在b的外部执行;
我们已经知道了作用域和作用域链的概念,上面代码的作用域是这样的:
a执行完,返回了b,此时的b只是声明,但还没调用,所以没有形成自己的AO,但作用域链和 a doing 时是一样的,所以虽然 a() 的作用域被销毁了,但是相同的一份却被b保存到了外面。这也就是内部函数被保存到了外面形成闭包的本质。这样也不难理解为什么上述代码打印出来的值是那样的了:
执行第一个demo()时,也即是执行 b(),由于b保存了a的作用域链,所以也可以访问到 num ,执行 b() 后,加一;
那为什么第二次执行 demo(),打印出的值还是有自增了呢?这是因为操作的都是保存在 b 里的 num ,虽然每次调用 demo() 都会形成新的作用域链,但是num,却是每次从上一次的作用域链直接 copy到当前作用域链中的。
这样形成的闭包虽然可以使外部可以访问到内部的函数,但是导致了原有的作用域链不释放,会造成内存泄漏。(内存泄漏的意思就是占用内存,可用内存资源变少了)。所以如果不是特殊需要,应尽量防止这种情况发生。
并且,作用域链的配置机制引出了一个值得注意的副作用:即闭包只取得包含函数中任何变量最后一个值,比如下面这个例子:
function createFunctions() { var result = []; for(let i = 0; i< 10; i++) { result[i] = function() { console.log(i); } } return result; } var myArr = createFunctions(); for(var j = 0; j < 10; j++) { myArr[j](); }
这个函数会返回一个函数数组,表面上看,似乎每个函数都应该有自己的索引值,即会返回:0,1,2...9;但实际上,每个函数都会返回10;这是因为在createFunctions()执行时,for循环跳出的条件是i=10;所以函数返回后,i的值是10 ;而每个result的作用域链中都保存这createFunctions()的AO,所以他们引用的都是createFunctions()的i值,所以每个函数内部i的值都是10;
这样,我们可以创建一个立即执行函数强制让闭包的行为符合预期:
function createFunctions() { var result = []; for(var i = 0; i < 10; i++) { (function(j) { result[i] = function() { document.write(j + " "); } }(i)); } return result; } var myArr = createFunctions(); for(var j = 0; j < 10; j++) { myArr[j](); }典型应用
下面看看几个用到闭包的典型例子:
实现共有变量
如累加器:调用多少次,累加多少次,用闭包更加模块化
function add() { var count = 0; function demo() { count++; console.log(count); } return demo; } var counter = add(); counter();//1 counter();//2 counter();//3
实现缓存
如eater: eat和push保存的都是eater的AO;,所以eat中food改变后。实际上是eater变了,所以也会影响push;
function eater() { var food = ""; var obj = { eat: function() { console.log("eating" + food); food = ""; }, push: function(myFood) { food = myFood; } } return obj;// 相当于返回里面的eat和push操作food; } var eater1 = eater(); eater1.push("banana"); eater1.eat();
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/100262.html
摘要:如何在初学就理解闭包你需要接着读下去。这样定义闭包是函数和声明该函数的词法环境的组合。小结闭包在中随处可见。闭包是中的精华部分,理解它需要具备一定的作用域执行栈的知识。 这是本系列的第 4 篇文章。 作为 JS 初学者,第一次接触闭包的概念是因为写出了类似下面的代码: for (var i = 0; i < helpText.length; i++) { var item = he...
摘要:闭包在我理解是一种比较抽象的东西。所以我写了一篇博文来方便自己理解闭包。那么现在我们可以解释一下闭包的第一个定义在计算机科学中,闭包是引用了自由变量的函数。循环中创建闭包在我们使用的关键字之前,闭包的一个常见问题就出现在循环中创建闭包。 零. 前言 从我开始接触前端时就听说过闭包,但是一直不理解闭包究竟是什么。上网看了各种博客,大家对闭包的说法不一。闭包在我理解是一种比较抽象的东西。所...
摘要:闭包是函数内部的子函数能读取局部变量二闭包的特点函数里面嵌套函数内部函数能访问外部函数的变量定义的参数和变量不会回收三闭包的前提先明白什么是全局变量和局部变量中声明变量格式关键字变量名标识符。建议在退出函数之前,将不使用的局部变量全部删除。 一、闭包的概念 闭包是指一个函数能够访问其函数外部作用域中的变量。JavaScript闭包是函数内部的子函数能读取局部变量 二、闭包的特点 函数...
摘要:说了半天,究竟什么是闭包呢闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。弹出上面函数中的函数就是闭包,就是通过建立函数来访问函数内部的局部变量。闭包会在父函数外部,改变父函数内部变量的值。 JavaScript的闭包 首先声明,这是一篇面向小白的博客,不过也欢迎各位大牛批评指正,谢谢。 其实关于闭包各个论坛社区里都有很多的文章来讲它,毕竟闭包是JavaScri...
摘要:说了半天,究竟什么是闭包呢闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。弹出上面函数中的函数就是闭包,就是通过建立函数来访问函数内部的局部变量。闭包会在父函数外部,改变父函数内部变量的值。 JavaScript的闭包 首先声明,这是一篇面向小白的博客,不过也欢迎各位大牛批评指正,谢谢。 其实关于闭包各个论坛社区里都有很多的文章来讲它,毕竟闭包是JavaScri...
阅读 1672·2021-10-28 09:32
阅读 570·2021-09-24 09:47
阅读 2892·2021-09-02 15:11
阅读 2717·2021-08-09 13:46
阅读 2869·2019-08-30 15:55
阅读 1054·2019-08-30 15:54
阅读 3267·2019-08-29 14:12
阅读 791·2019-08-26 13:40