博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
简单理解 JavaScript 闭包问题
阅读量:6159 次
发布时间:2019-06-21

本文共 2722 字,大约阅读时间需要 9 分钟。

从我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();

转载地址:http://rnsfa.baihongyu.com/

你可能感兴趣的文章
MySql操作
查看>>
python 解析 XML文件
查看>>
MySQL 文件导入出错
查看>>
java相关
查看>>
由一个异常开始思考springmvc参数解析
查看>>
向上扩展型SSD 将可满足向外扩展需求
查看>>
虚机不能启动的特例思考
查看>>
SQL Server编程系列(1):SMO介绍
查看>>
在VMware网络测试“专用VLAN”功能
查看>>
使用Formik轻松开发更高质量的React表单(三)<Formik />解析
查看>>
也问腾讯:你把用户放在什么位置?
查看>>
CSS Sprites 样式生成工具(bg2css)
查看>>
[转]如何重构代码--重构计划
查看>>
类中如何对list泛型做访问器??
查看>>
C++解析XML--使用CMarkup类解析XML
查看>>
P2P应用层组播
查看>>
Sharepoint学习笔记—修改SharePoint的Timeouts (Execution Timeout)
查看>>
CSS引入的方式有哪些? link和@import的区别?
查看>>
Redis 介绍2——常见基本类型
查看>>
asp.net开发mysql注意事项
查看>>