JavaScript闭包的用法和场景
Posted 三水草肃
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript闭包的用法和场景相关的知识,希望对你有一定的参考价值。
这里写目录标题
闭包
闭包的形成与变量的作用域以及变量的生存周期密切相关。
变量的作用域
变量的作用域就是指变量的有效范围。 我们最常谈到的是在函数中声明的变量作用域。
- 在定义变量的时候尽量使用 ES6 的 let 和 const,因为 let 和 const 是块作用域,var 不是。var 容易造成命名冲突、
- 变量的搜索是从内到外的
- 变量的生命周期:
- 全局变量的生命周期是永久的
- 局部变量随着函数销毁而销毁
闭包的形成
当局部变量被其他使用,那么局部变量就有不用销毁的理由,就会形成闭包。
例子
for (var i = 0; i < 5; i++)
setTimeout(() =>
console.log(i); // 5 5 5 5 5
);
- 最后结果是 5 个 5,是因为 setTimeout 是异步,当执行 setTimeout 内部事件的时候,for 循环已经结束,因为 var 是全局作用域,在 for 循环中 i 是公用的,所以 for 循环之后变量 i 的值是 5。
- 解决方法:
- 闭包,使用立即执行函数 IIFE,把每次循环的 i 封闭起来。当变量顺着从内到外查找时,会先找到被封闭在闭包环境中的 i(或许是 IIFE 生成 N 个函数进栈,然后一个一个蹦出来)
for (var i = 0; i < 5; i++) (function (j) setTimeout(() => console.log(j); // 0 1 2 3 4 ); )(i);
- 使用 let,每个 i 形成块级作用域,互不干扰。
for (let i = 0; i < 5; i++) setTimeout(() => console.log(i); // 0 1 2 3 4 );
闭包的使用场景
- 封装变量
- 闭包可以帮助不需要暴露在全局的变量封装成私有变量。
- 提炼函数时代码重构的一种常见技巧。在开发中常常把大函数中的代码块独立出来,独立出来的函数也有助于代码复用。
- 如下例子,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
- 延长局部变量的寿命
img 对象进行数据上报或埋点的时候,不能保证每次都能上传成功。当函数调用结束,局部变量也随之销毁,而此时还没来得及发送 HTTP 请求,所以请求就会丢失掉。 - 解决方法:用闭包把变量封装起来
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);
,
;
闭包与内存管理
- 内存泄漏
闭包跟内存泄漏有关系的点是 使用闭包容易形成循环引用,如果闭包的作用域链保存 DOM 节点,有可能会造成内存泄漏。
原因:当浏览器使用引用计数策略的垃圾回收机制,在引用计数策略的垃圾回收机制中两个对象形成了循环引用,那么这两个对象都无法被回收,会造成内存泄漏 - 占用内存
局部变量被封闭在闭包形成的环境中,局部变量不会被销毁。对内存有一定的影响,全局变量也是这个道理。如果将来需要回收这些变量,我们可以手动把这些变量设为 null,不会造成内存泄漏
javascript设计模式与开发实践 曾探
以上是关于JavaScript闭包的用法和场景的主要内容,如果未能解决你的问题,请参考以下文章