什么是JavaScript中的闭包?

来源:IPIPP.com作者:头衔:全栈工程师
导读:本期聚焦于小伙伴创作的《什么是JavaScript中的闭包?》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《什么是JavaScript中的闭包?》有用,将其分享出去将是对创作者最好的鼓励。

闭包是JavaScript中一个非常核心的概念,很多开发者刚接触时都会觉得难以理解,甚至在实际写代码时已经用到了闭包却完全没有意识到。接下来我们会从基础概念开始,一步步拆解闭包的相关知识。

什么是JavaScript中的闭包?

闭包的基本定义

在JavaScript中,闭包指的是有权访问另一个函数作用域中变量的函数,简单来说,就是当内部函数被保存到外部时,就会生成闭包。闭包的本质是内部函数可以记住并访问它的词法作用域,即使这个函数在它的词法作用域之外执行。

要理解闭包,首先需要知道JavaScript的作用域规则:JavaScript采用的是词法作用域,也就是函数的作用域在函数定义的时候就已经确定了,和函数调用的位置没有关系。

闭包的形成条件

闭包的形成需要满足三个基本条件:

  • 存在嵌套的函数结构,也就是一个函数内部定义了另一个函数
  • 内部函数引用了外部函数的变量或者参数
  • 内部函数被外部函数返回,或者在外部函数的外部被调用

闭包的基础示例

我们来看一个最简单的闭包示例,帮助理解闭包的工作方式:

// 外部函数
function outer() {
    // 外部函数的局部变量
    let count = 0;
    // 内部函数,引用了外部函数的count变量
    function inner() {
        count++;
        console.log(count);
    }
    // 返回内部函数,此时inner被保存到外部,形成闭包
    return inner;
}

// 调用outer得到inner函数
const counter = outer();
// 调用counter,此时inner在outer的作用域之外执行,依然可以访问count
counter(); // 输出1
counter(); // 输出2
counter(); // 输出3

上面的代码中,inner函数引用了outer函数的count变量,并且outer函数返回了inner函数。当我们调用outer得到counter之后,outer函数的执行上下文已经出栈,但是count变量并没有被销毁,因为inner函数形成的闭包还在引用它,每次调用counter都会修改这个count的值。

闭包的常见应用场景

1. 实现数据私有化

闭包可以让变量不被外部直接访问和修改,只能通过特定的方法操作,实现类似面向对象中私有属性的效果:

function createPerson() {
    // 私有变量,外部无法直接访问
    let name = '张三';
    return {
        // 公开方法,用于获取私有变量
        getName: function() {
            return name;
        },
        // 公开方法,用于修改私有变量
        setName: function(newName) {
            name = newName;
        }
    };
}

const person = createPerson();
console.log(person.getName()); // 输出张三
person.setName('李四');
console.log(person.getName()); // 输出李四
// 直接访问name会返回undefined,因为name是私有变量
console.log(person.name); // 输出undefined

2. 函数柯里化

函数柯里化是把接受多个参数的函数转换成接受一个单一参数,并且返回接受余下的参数而且返回结果的新函数的技术,闭包是实现柯里化的核心:

// 普通的多参数求和函数
function sum(a, b, c) {
    return a + b + c;
}

// 柯里化后的函数
function currySum(a) {
    return function(b) {
        return function(c) {
            return a + b + c;
        };
    };
}

// 调用方式
console.log(currySum(1)(2)(3)); // 输出6

// 也可以分步调用
const addOne = currySum(1);
const addOneAndTwo = addOne(2);
console.log(addOneAndTwo(3)); // 输出6

3. 防抖和节流

防抖和节流是前端开发中常用的性能优化手段,它们的实现也依赖闭包来保存定时器或者上次执行的时间等状态:

// 防抖函数,连续触发事件时,只在最后一次触发后等待wait时间执行
function debounce(fn, wait) {
    let timer = null; // 用闭包保存定时器变量
    return function(...args) {
        // 每次触发先清除之前的定时器
        if (timer) clearTimeout(timer);
        // 重新设置定时器
        timer = setTimeout(() => {
            fn.apply(this, args);
        }, wait);
    };
}

// 节流函数,连续触发事件时,每隔一段时间只执行一次
function throttle(fn, interval) {
    let lastTime = 0; // 用闭包保存上次执行的时间
    return function(...args) {
        const now = Date.now();
        // 如果距离上次执行的时间大于等于间隔时间,就执行函数
        if (now - lastTime >= interval) {
            lastTime = now;
            fn.apply(this, args);
        }
    };
}

闭包的注意事项

虽然闭包非常有用,但是使用的时候也需要注意一些问题:

  • 闭包会持有外部函数的作用域引用,导致这些作用域中的变量无法被垃圾回收,如果滥用闭包可能会造成内存泄漏,不需要的时候要及时解除对闭包的引用
  • 在循环中使用闭包的时候要特别注意作用域的问题,比如经典的循环绑定事件问题,需要正确使用闭包或者let块级作用域来解决

总的来说,闭包是JavaScript中非常强大的特性,理解闭包的原理和使用场景,能帮助我们写出更灵活、更优雅的代码,也能更好地理解很多JavaScript库和框架的实现逻辑。

JavaScript闭包作用域函数变量修改时间:2026-05-29 23:41:38

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。