JS—闭包:3分钟从入门到放弃

个人博客:haichenyi.com。感谢关注

一. 目录

二. 基础定义

  闭包:能够访问外部函数作用域的函数,以及其词法环境的组合。举个老生常谈栗子:

function createCounter() {
    let count = 0; // 被闭包捕获的变量
    
    return function() {//内部大括号
        count += 1;
        return count;
    };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2

  解析一下上面这段代码:

  1. 上面这个例子,有两个方法,一个是外部的createCounter方法,一个是内部的return 的方法。
  2. 外部createCounter方法定义了一个变量count
  3. 内部return的方法,使用了这个变量,让这个变量+1,然后返回。
  4. 我们都知道内部可以访问外部变量,这是没有问题的。反过来,外部不能访问内部变量(var除外)
      我们再来说这个概念。
    a. 访问外部函数作用域的函数,指的就是上面例子中的return返回的函数
    b. 词法环境,简单的理解就是作用域,也就是两个大括号之间的东西(不够严谨,可以简单的这么理解)

三. 闭包的运行机制

3.1 词法作用域链

function outer() {
    const x = 10;
    
    function inner() {
        console.log(x); // 访问父作用域的x
    }
    
    return inner;
}
  • 函数定义时确定作用域链
  • 闭包在outer执行结束后依然保留对x的访问

3.2 内存模型解析

function createClosure() {
    const bigData = new Array(1000000); // 大数据
    
    return () => console.log(bigData.length);
}

const closure = createClosure();
  • 闭包持有对父作用域整个变量对象的引用
  • 错误的闭包使用会导致bigData无法被GC回收

四. 闭包实战应用场景

4.1 模块模式(现代ES6替代方案export,import)

const calculator = (() => {
    let memory = 0;
    
    return {
        add: (x) => memory += x,
        reset: () => memory = 0
    };
})();

calculator.add(5); // 5
calculator.reset();

对标ES6的export,import

//tool.js
let memory = 0
export function add(x) {
	return memory +x
}
export function reset() {
	memory =0
}
//在需要用的位置import tool.js文件的add和reset两个方法即可

五. 内存泄漏预防指南

5.1 常见泄漏场景

  • DOM元素引用
function createClosure() {
    const element = document.getElementById('bigElement');
    
    return () => {
        console.log(element.offsetWidth);
    };
}
  • 未清理的定时器
function startTimer() {
    const data = fetchData();
    
    setInterval(() => {
        process(data); // data被长期持有
    }, 1000);
}

5.2 优化策略

  • ​解除引用:在不再需要时手动置null
function cleanClosure() {
    let data = /* ... */;
    
    const cleanup = () => {
        data = null;
    };
    
    return { processData, cleanup };
}
  • 使用WeakMap/WeakRef
const weakCache = new WeakMap();

function cacheData(element, data) {
   weakCache.set(element, data);
}
  • ​事件监听器管理
function setupListeners() {
   const handler = () => {/* ... */};
   
   element.addEventListener('click', handler);
   
   return () => {
       element.removeEventListener('click', handler);
   };
}

六. 最佳实践总结

  1. ​最小化闭包作用域
    • 只保留必要的变量
    • 及时解除无用引用
  2. 优先使用块级作用域
//在类似于这种循环时,使用let,const,不要使用var
for (let i = 0; i < 10; i++) {
    setTimeout(() => console.log(i), 100);
}
  1. 模块化代码结构
// ES Modules
export const createClosure = () => { /* ... */ };
//也就是使用ES6的export 和 import
  1. 配合垃圾回收机制:避免循环引用;使用WeakMap代替普通对象
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海晨忆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值