当前位置: 首页 > article >正文

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代替普通对象

http://www.kler.cn/a/582573.html

相关文章:

  • 数据结构:排序详解(使用语言:C语言)
  • 赶紧白P这款免费神器!
  • 差分数组题目
  • 机器学习(吴恩达)
  • 有关MyBatis的缓存(一级缓存和二级缓存)
  • 【第四节】windows sdk编程:windows 中的窗口
  • 基于Python+SQLite实现校园信息化统计平台
  • java校验String是否符合时间格式 yyyy-MM-dd HH:mm:ss
  • vs2022用git插件重置--删除更改(--hard)后恢复删除的内容
  • Qt 6.6.1 中 QPixmap::grabWindow() 的用法与替代方案
  • Spring之生命周期Bean的生成过程
  • python-leetcode-K 和数对的最大数目
  • 【Godot4.3】RenderingServer总结
  • c++介绍运算符重载九
  • vscode接入DeepSeek 免费送2000 万 Tokens 解决DeepSeek无法充值问题
  • 5秒学会excel中序号列自动增加,不是拖动,图解加说明,解决序号自增多了手拖太累
  • VSTO(C#)Excel开发5:调整表格到一页
  • 【ELK】ElasticSearch 集群常用管理API操作
  • ChebyKAN0、ChebyKAN1 网络阅读
  • 常用Kotlin方法