• javascript中重要概念-闭包-深入理解


    在上次的分享中javascript--函数参数与闭包--详解,对闭包的解释不够深入。本人经过一段时间的学习,对闭包的概念又有了新的理解。于是便把学习的过程整理成文章,一是为了加深自己闭包的理解,二是给读者提供学习的途径,避免走弯路。

    javascript--函数参数与闭包--详解这篇文章中,我详细介绍了闭包的概念。以下的分享对闭包的基本概念只会稍稍带过。如果对闭包的概念不熟悉的同学,请移步至javascript--函数参数与闭包--详解

    以下的分享会分为如下内容:

    1.let命令

    2.闭包特点的解读

    3.循环中的闭包

    1.let命令

      在讲闭包前,有必要谈谈ES6中的新概念,let命令。因为在赘述循环中的闭包时会使用到let命令。

      基本用法

      ES6新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。

    复制代码
    1     if (true) {
    2         var a = 1;
    3         let b = 2;
    4     }
    5     console.log(a); // 1
    6     console.log(b); // ReferenceError: b is not defined
    复制代码

      在javascript--函数参数与闭包--详解中,谈到在局部变量只能在函数内部声明,在其他代码(如 if 条件语句,for循环语句)用 var 声明的变量都为全局变量。

      在上面代码中,分别用 let和 var 声明了两个变量。然后在代码块之外调用这两个变量,结果 let 声明的变量报错,var 声明的变量返回了正确的值。这表明,if 条件语句中使用var声明的变量为全局变量,可以在全局作用域下访问。而 let 声明的变量只在它所在的代码块有效,在全局作用域下无法访问。

      再来看看这两个例子。

    1     for (let i = 0; i < 10; i++) {}
    2     console.log(i);  //ReferenceError: i is not defined
    1     for (var i = 0; i < 10; i++) {}
    2     console.log(i);  // 10

    2.闭包特点的解读

      我们知道,闭包有三个特点

      a:在一个函数内部定义另外一个函数。

      b:内部函数可以访问外部函数定义的局部变量 (变量采用var声明)

      c:让局部变量始终保存在内存中。也就是说,闭包可以使得它诞生的环境一直存在。

      我们来看一个例子,尝试串起这三个特点。

    复制代码
     1     function keith() {
     2         var a = 1;
     3         return function() {
     4             return a++;
     5         }
     6     }
     7     var result = keith();
     8     console.log(result()); //1
     9     console.log(result()); //2
    10     console.log(result()); //3
    复制代码

      首先,在函数keith内部返回了一个匿名函数,如果函数keith没有返回值,则默认返回值为undefined。

      然后,因为在函数keith中返回了一个匿名函数,又把调用函数keith的结果赋值给了全局变量result,所以全局变量result是一个闭包。当连续调用result时,依次返回1,2,3。返回值说明了内部函数可以访问外部函数定义的局部变量。也就是说,闭包记住了外部函数定义的局部变量的调用结果。

      最后,因为我们把一个闭包赋值给了一个全局变量result,在调用时依次输出1,2,3。说明了在函数keith外部访问的这个局部变量a一直存在全局作用域中。也就是说,局部变量 a 一直存在于内存当中,所以不会被垃圾回收机制回收。

      所以使用闭包的时候的注意点:

      由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

    3.循环中的闭包

      一个常见的错误出现在循环中使用闭包,假设我们需要在每次循环中调用循环序号。

    1     for (var i = 0; i < 10; i++) {
    2         setTimeout(function() {
    3             console.log(i); //10
    4         }, 1000)
    5     }

      上面代码中,不会符合我们的预期,输出数字0-9。而是会输出数字10十次。

      出现错误的原因在于我们在setTimeout函数里面定义了一个匿名函数,匿名函数的作用是在控制台输出变量 i,而变量 i 是一个全局变量,在全局范围内都有效。所以每一次循环,新的 值都会覆盖旧值,导致最后输出的是最后一轮的i的值。

      所以,针对循环中的闭包,有以下两种解决方法。

      一是使用立即执行函数(IIFE),并把 i 作为它的参数,此时函数内 e 变量就拥有了 i 的一个拷贝。当传递给 setTimeout 的匿名函数执行时,它就拥有了对 e 的引用,而这个值是不会被循环改变的。

    复制代码
    1     for (var i = 0; i < 10; i++) {
    2         (function(e){
    3             setTimeout(function() {
    4                 console.log(e); //1,2,3,....,10
    5             }, 1000)
    6         })(i)
    7     }
    复制代码

      二是让变量 i 只在代码块中有效。也就是说让其成为局部变量。变量 是 let 声明的,当前的 只在本轮循环有效,所以每一次循环的 其实都是一个新的变量,所以最后输出的是1,2,3,4....,10。

    1     for (let i = 0; i < 10; i++) {
    2         setTimeout(function() {
    3             console.log(i); //1,2,3...,10
    4         }, 1000)
    5     }

      

  • 相关阅读:
    好用的PHP读取EXCEL类
    PHP获取函数参数数组
    在 Win7 下运行 TC 2.0 / TC3.0 / BC 3.1 / QB 4.5 等 DOS 开发工具
    用PHP生成等比图像的方法
    判断当前发布日期是否超过今天
    HTTP响应代码中文详解
    __autoload自动加载函数
    __isset魔术方法
    php下载远程文件类(支持断点续传)
    用PHP保存从摄像头拍下来的图片
  • 原文地址:https://www.cnblogs.com/libin-1/p/5801265.html
Copyright © 2020-2023  润新知