JavaScript闭包的用法和场景

Posted 三水草肃

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript闭包的用法和场景相关的知识,希望对你有一定的参考价值。

这里写目录标题

闭包

闭包的形成与变量的作用域以及变量的生存周期密切相关。

变量的作用域

变量的作用域就是指变量的有效范围。 我们最常谈到的是在函数中声明的变量作用域。

  1. 在定义变量的时候尽量使用 ES6 的 let 和 const,因为 let 和 const 是块作用域,var 不是。var 容易造成命名冲突、
  2. 变量的搜索是从内到外的
  3. 变量的生命周期:
    1. 全局变量的生命周期是永久的
    2. 局部变量随着函数销毁而销毁

闭包的形成

当局部变量被其他使用,那么局部变量就有不用销毁的理由,就会形成闭包。

例子

for (var i = 0; i < 5; i++) 
  setTimeout(() => 
    console.log(i); // 5 5 5 5 5
  );

  1. 最后结果是 5 个 5,是因为 setTimeout 是异步,当执行 setTimeout 内部事件的时候,for 循环已经结束,因为 var 是全局作用域,在 for 循环中 i 是公用的,所以 for 循环之后变量 i 的值是 5。
  2. 解决方法:
    1. 闭包,使用立即执行函数 IIFE,把每次循环的 i 封闭起来。当变量顺着从内到外查找时,会先找到被封闭在闭包环境中的 i(或许是 IIFE 生成 N 个函数进栈,然后一个一个蹦出来)
    for (var i = 0; i < 5; i++) 
      (function (j) 
        setTimeout(() => 
          console.log(j); // 0 1 2 3 4
        );
      )(i);
    
    
    1. 使用 let,每个 i 形成块级作用域,互不干扰。
    for (let i = 0; i < 5; i++) 
      setTimeout(() => 
        console.log(i); // 0 1 2 3 4
      );
    
    

闭包的使用场景

  1. 封装变量
    1. 闭包可以帮助不需要暴露在全局的变量封装成私有变量。
    2. 提炼函数时代码重构的一种常见技巧。在开发中常常把大函数中的代码块独立出来,独立出来的函数也有助于代码复用。
    3. 如下例子,cache 仅仅在 mutle 中使用,我们可以把 cache 封闭在 mult 函数内部,减少页面中的全局变量。
const cache = ;
let mult = function () 
  const args = Array.prototype.join.call(arguments, "");
  console.log(args);
  if (cache[args]) 
    return cache[args];
  
  let a = 1;
  for (let i = 0, l = args.length; i < l; i++) 
    a = a * arguments[i];
  
  return (cache[args] = a);
;
console.log(mult(1, 2, 3)); // 6
let mult = (function () 
  const cache = ;
  return function () 
    const args = Array.prototype.join.call(arguments, "");
    console.log(args);
    if (cache[args]) 
      return cache[args];
    
    let a = 1;
    for (let i = 0, l = args.length; i < l; i++) 
      a = a * arguments[i];
    
    return (cache[args] = a);
  ;
)();
console.log(mult(1, 2, 3)); // 6
let mult = (function () 
  const cache = ;
  const calculate = function () 
    let a = 1;
    for (let i = 0, l = arguments.length; i < l; i++) 
      a = a * arguments[i];
    
    return a;
  ;
  return function () 
    const args = Array.prototype.join.call(arguments, "");
    console.log(args);
    if (cache[args]) 
      return cache[args];
    
    return (cache[args] = calculate.apply(null, arguments));
  ;
)();
console.log(mult(1, 2, 3)); // 6
  1. 延长局部变量的寿命
    img 对象进行数据上报或埋点的时候,不能保证每次都能上传成功。当函数调用结束,局部变量也随之销毁,而此时还没来得及发送 HTTP 请求,所以请求就会丢失掉。
  2. 解决方法:用闭包把变量封装起来
const report = (function () 
  const imgs = [];
  return function (src) 
    console.log(src);
    let img = new Image();
    imgs.push(img);
    img.src = src;
  ;
)();

闭包和面向对象设计

过程和数据的结合是形容面向对象经常使用的表达,对象以方法的形式包含过程,而闭包则是在过程中爆了数据。面向对象能实现的,闭包也可以实现。

// 闭包
const extendsb = function () 
  var value = 0;
  return 
    call: function () 
      value++;
      console.log(value);
    ,
  ;
;
// 面向对象
const extendso = 
  value: 0,
  call: function () 
    this.value++;
    console.log(this.value);
  ,
;

闭包与内存管理

  1. 内存泄漏
    闭包跟内存泄漏有关系的点是 使用闭包容易形成循环引用,如果闭包的作用域链保存 DOM 节点,有可能会造成内存泄漏。
    原因:当浏览器使用引用计数策略的垃圾回收机制,在引用计数策略的垃圾回收机制中两个对象形成了循环引用,那么这两个对象都无法被回收,会造成内存泄漏
  2. 占用内存
    局部变量被封闭在闭包形成的环境中,局部变量不会被销毁。对内存有一定的影响,全局变量也是这个道理。如果将来需要回收这些变量,我们可以手动把这些变量设为 null,不会造成内存泄漏

javascript设计模式与开发实践 曾探

以上是关于JavaScript闭包的用法和场景的主要内容,如果未能解决你的问题,请参考以下文章

闭包

闭包

07.30《JavaScript》——闭包和简单模块

《JavaScript设计模式与开发》笔记 4.闭包

js 闭包 具体作用?

JavaScript闭包的概念及用法