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

JavaScript高级特性剖析:闭包

在这里插入图片描述

JavaScript高级特性剖析:闭包

  • 一,闭包的概念
      • 前言
      • 1,核心定义
      • 2,关键特性
  • 二,闭包的原理
  • 三,闭包的用法
  • 四,闭包的注意事项
  • 五,总结

一,闭包的概念

前言

闭包(Closure)是 JavaScript 的一个核心特性,它是 JavaScript 语言设计的一部分,从 JavaScript 诞生之初就存在。闭包的概念并不是 JavaScript 独有的,但它在 JavaScript 中表现得尤为突出和重要。

在js中因为闭包特性的存在,这些功能才得以实现:

  • 数据封装:通过闭包可以实现私有变量和方法。
  • 回调函数:闭包使得回调函数可以访问定义时的上下文。
  • 函数柯里化:闭包是实现函数柯里化(Currying)的基础。
  • 模块化:闭包是 JavaScript 模块化的基础。

1,核心定义

  • 闭包是函数和其创建时的词法环境(lexical environment) 的组合。简单来说:
    当一个函数可以记住并访问它定义时的作用域,即使这个函数在其父作用域之外执行,就形成了闭包。
  • 闭包允许函数“捕获”它被创建时的上下文中的变量。

2,关键特性

  1. 跨作用域访问:当一个函数(内部函数)能访问其声明时的作用域链中的变量,即使这个函数在其父作用域之外执行。
  2. 环境保留:闭包会保留对其词法环境的引用,使得这些变量不会被垃圾回收机制回收。

二,闭包的原理

当了解了特性后,我们继续探讨其原理:

  1. JavaScript 的作用域是静态作用域,函数的作用域在定义时就已经被确定,而不是在执行时。
  2. 当函数内部定义另一个函数时,内部函数会持有外部函数的作用域引用。
  3. 即使外部函数执行完毕,如果内部函数仍然存在(例如被返回、保存或作为回调),外部函数的变量不会被垃圾回收,因为内部函数可能还会访问它们。

三,闭包的用法

  1. 示例 1:最简单的闭包
function outer() {
    let count = 0;

    function inner() {
        count++; 
        console.log(count);
    }
    
    return inner;
}

const counter = outer();
counter(); // 输出 1
counter(); // 输出 2

分析:inner 函数记住了它定义时的环境(count 变量)每次调用 counter() 时,count 的值会被保留下来。

  1. 示例 2:私有变量封装
function createCounter() {
  let count = 0; // 私有变量

  return {
    increment: function() { count++; },
    getCount: function() { return count; },
  };
}

const counter = createCounter();
counter.increment();
console.log(counter.getCount()); // 输出 1
console.log(counter.count); // 输出 undefined(无法直接访问)

分析:count 被封装在闭包中,只能通过 increment 和 getCount 方法操作。

  1. 示例 3:回调函数和事件处理
function delayMessage(message, delay) {
  setTimeout(function() {
    console.log(message); 
  }, delay);
}

delayMessage("delay 2s", 2000);

分析:闭包保留了 message 的引用

  1. 示例 4:柯里化函数
// 普通函数
function add(a, b, c) {
    return a + b + c;
}
console.log(add(1, 2, 3)); // 输出 6

// 柯里化后的函数(分步传参)
function addCurried(a) {
    return function(b) {
        return function(c) {
            return a + b + c;
        };
    };
}

// 调用方式
const step1 = addCurried(1);    // 返回一个函数,记住 a=1
const step2 = step1(2);         // 返回一个函数,记住 b=2
const result = step2(3);        // 最终计算 1+2+3=6
console.log(result); // 输出 6

// 也可以链式调用
console.log(addCurried(1)(2)(3)); // 输出 6

函数柯里化是一种将多参数函数转换为单参数函数链的技术。
具体来说,柯里化后的函数会逐步接收参数,每次接收一个参数,并返回一个新的函数来接收下一个参数,直到所有参数收集完毕,最终返回结果。
可以看到在js中柯里化就是利用了js的闭包特性
柯里化的核心思想
分步传递参数:将一次性传递多个参数的方式,改为分步传递。
延迟执行:柯里化后的函数可以延迟到所有参数收集完毕后再执行。
复用性:可以复用部分参数,生成更具体的函数。

柯里化函数具体应用场景举例:

  1. 记录日志
function createLogger(prefix) {
    // 通过闭包记住
    return function(message) {
        console.log(`[${prefix}] ${message}`);
    };
}

const infoLogger = createLogger("INFO");
infoLogger("INFO LOG OK"); // [INFO] INFO LOG OK

const errorLogger = createLogger("ERROR");
errorLogger("ERROR LOG OK"); // [ERROR] ERROR LOG OK
  1. 范围判断
// 柯里化校验函数
function validate(min) {
    return function(max) {
        return function(value) {
            return value >= min && value <= max;
        };
    };
}

// 校验值是否在 13~60 之间
const validateAge = validate(13)(60);
console.log(validateAge(13)); // true
console.log(validateAge(111)); // false
  1. 示例 5:模块化开发
const module = (function() {
  let privateVar = 10;

  function privateMethod() {
    return privateVar * 2;
  }

  return {
    publicMethod: function() {
      return privateMethod();
    }
  };
})();

console.log(module.publicMethod()); // 输出 20

分析:这段代码通过 立即执行函数(IIFE) 和 闭包实现了模块化。
私有变量和函数被封装在模块内部,外部无法直接访问。
公有方法作为接口,通过闭包间接操作私有成员。

四,闭包的注意事项

从闭包的特性和底层原理:“外部函数执行完毕,如果内部函数仍然存在(例如被返回、保存或作为回调),外部函数的变量不会被垃圾回收。”来看,使用闭包特性需要避免内存泄漏风险,如果变量不再需要,切记手动解除引用:

function buildLargeArray() {
  let bigData = new Array(1000000).fill("*"); // 大数组

  return {
    process: function() {
      console.log("process",bigData);
    },
    clear: function() {
      bigData = null; // 手动解除引用
      console.log("clear",bigData);
    },
  };
}

const processor = buildLargeArray();
processor.process(); // 执行任务
processor.clear(); // 手动解除引用

五,总结

一句话:
闭包就是让函数记住自己出生的地方,使用闭包要注意内存泄露问题。

相关文章:

  • 前端开发怎么处理数据的安全
  • 类型断言, 类型注解
  • Javaweb后端spring事务管理 事务四大特性ACID
  • AI档案审核2
  • 操作系统 2.9-进程同步和信号量
  • Android A/B System OTA分析提取 payload 在ZIP包中的 offset 和 size
  • 电脑网络出现问题!简单的几种方法解除电脑飞行模式
  • 解决AWS EC2实例无法使用IAM角色登录AWS CLI
  • Gradle本地配置文件分享
  • 【大模型基础_毛玉仁】2.2 大语言模型架构概览
  • DeepSeek教我写词典爬虫获取单词的音标和拼写
  • 鸿蒙生态日日新,夸克、顺丰速运、驾校一点通等多款应用功能更新
  • [FE] React 初窥门径(五):React 组件的加载过程(commit 阶段)
  • Doris vs ClickHouse 企业级实时分析引擎怎么选?
  • C# 常用数据类型
  • C语言_数据结构总结3:带头结点的单链表
  • MAC电脑常用操作
  • Nginx的反向代理(超详细)
  • 历史脉络总结;夏商周的主要特征
  • Springboot基础篇(4):自动配置原理
  • 了解网站基本知识/百度提交收录入口
  • 网站建设服务费怎么做会计分录/聊城优化seo
  • 加工厂网站建设/信息流广告的特点
  • 湘潭网站制作公司/南宁今日头条最新消息
  • 天河建设网站系统/怎么让百度收录
  • 深圳做官网的公司/seo公司 上海