导读:本期聚焦于小伙伴创作的《如何理解JavaScript中的闭包概念_它在实际项目中有哪些应用场景?》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何理解JavaScript中的闭包概念_它在实际项目中有哪些应用场景?》有用,将其分享出去将是对创作者最好的鼓励。

闭包是JavaScript中基于词法作用域产生的特性,指的是函数可以记住并访问其词法作用域,即使这个函数在其词法作用域之外执行。要理解闭包,首先需要明确JavaScript的作用域规则,函数的作用域在函数定义的时候就已经确定了,和函数调用的位置没有关系。

如何理解JavaScript中的闭包概念_它在实际项目中有哪些应用场景?

闭包的核心原理

JavaScript的函数在创建的时候,会保存一个包含其词法作用域的内部属性,当函数执行的时候,如果内部定义了新的函数,这个内部函数就会引用外部函数的作用域链。当外部函数执行完毕之后,正常情况下它的作用域会被销毁,但是如果内部函数被返回或者在其他地方被引用,那么外部函数的作用域就不会被释放,内部函数依然可以访问外部函数中的变量,这就是闭包的形成过程。

我们可以通过一个简单的代码示例来理解闭包的形成:

// 外部函数
function outer() {
    // 外部函数的局部变量
    let count = 0;
    // 内部函数,访问了外部函数的count变量
    function inner() {
        count++;
        console.log(count);
    }
    // 返回内部函数
    return inner;
}

// 调用outer函数,得到inner函数的引用
const counter = outer();
// 调用counter,此时outer函数已经执行完毕,但是依然可以访问count变量
counter(); // 输出1
counter(); // 输出2
counter(); // 输出3

上面的代码中,outer函数执行之后返回了inner函数,并且赋值给了counter变量。outer函数执行完毕之后,正常情况下它的作用域应该被销毁,但是因为inner函数引用了outer函数作用域中的count变量,所以outer函数的作用域被保留了下来,每次调用counter的时候,都可以访问并修改count的值,这就是典型的闭包场景。

闭包的实际项目应用场景

1. 封装私有变量

JavaScript本身没有私有属性的语法支持,但是可以通过闭包实现私有变量的封装,避免外部直接修改内部状态,保证数据的安全性。

function createPerson(name) {
    // 私有变量,外部无法直接访问
    let age = 0;
    return {
        // 公开方法,可以访问私有变量
        getAge: function() {
            return age;
        },
        setAge: function(newAge) {
            if (newAge >= 0 && newAge <= 150) {
                age = newAge;
            } else {
                console.log('年龄不合法');
            }
        },
        getName: function() {
            return name;
        }
    };
}

const person = createPerson('张三');
console.log(person.getName()); // 输出张三
person.setAge(20);
console.log(person.getAge()); // 输出20
// 无法直接访问age变量
console.log(person.age); // 输出undefined

上面的代码中,age变量是createPerson函数作用域中的局部变量,外部无法直接访问,只能通过返回的对象中的getAgesetAge方法来操作,实现了私有变量的封装。

2. 函数柯里化

函数柯里化指的是将一个接收多个参数的函数,转换成接收一个单一参数(最初函数的第一个参数)的函数,并且返回接收余下的参数而且返回结果的新函数的技术,闭包可以很方便地实现函数柯里化。

// 普通的加法函数
function add(a, b, c) {
    return a + b + c;
}

// 柯里化函数
function curry(fn) {
    return function curried(...args) {
        // 如果传入的参数个数大于等于原函数的参数个数,直接执行原函数
        if (args.length >= fn.length) {
            return fn.apply(this, args);
        } else {
            // 否则返回一个新的函数,继续接收参数
            return function(...args2) {
                return curried.apply(this, args.concat(args2));
            };
        }
    };
}

const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 输出6
console.log(curriedAdd(1, 2)(3)); // 输出6
console.log(curriedAdd(1)(2, 3)); // 输出6

柯里化之后的函数可以分步传递参数,每次传递参数之后返回的新函数通过闭包保存了之前传入的参数,直到参数足够的时候再执行原函数,这种特性在很多需要延迟计算的场景中非常有用。

3. 防抖和节流函数实现

防抖和节流是前端开发中常用的性能优化手段,两者的实现都离不开闭包,通过闭包保存定时器或者上次执行的时间状态。

防抖函数的实现如下:

function debounce(fn, delay) {
    // 用闭包保存定时器变量
    let timer = null;
    return function(...args) {
        // 每次调用的时候清除之前的定时器
        if (timer) {
            clearTimeout(timer);
        }
        // 重新设置定时器
        timer = setTimeout(() => {
            fn.apply(this, args);
            timer = null;
        }, delay);
    };
}

// 使用防抖函数,比如输入框搜索场景
const input = document.querySelector('input');
input.addEventListener('input', debounce(function(e) {
    console.log('搜索内容:', e.target.value);
}, 500));

节流函数的实现如下:

function throttle(fn, interval) {
    // 用闭包保存上次执行的时间
    let lastTime = 0;
    return function(...args) {
        const now = Date.now();
        // 如果距离上次执行的时间大于等于间隔时间,才执行函数
        if (now - lastTime >= interval) {
            fn.apply(this, args);
            lastTime = now;
        }
    };
}

// 使用节流函数,比如滚动加载场景
window.addEventListener('scroll', throttle(function() {
    console.log('滚动事件触发');
}, 300));

4. 循环中获取正确的变量值

在ES6之前,使用var声明变量的循环中,很容易出现变量共享的问题,闭包可以解决这个问题,不过现在更推荐使用let声明变量,因为let会形成块级作用域。

用闭包解决循环问题的示例:

// 使用var声明的循环,直接用闭包保存每次循环的i值
for (var i = 0; i < 5; i++) {
    (function(index) {
        setTimeout(function() {
            console.log(index);
        }, 1000);
    })(i);
}
// 依次输出0 1 2 3 4

闭包的注意事项

闭包虽然有很多有用的场景,但是也不能滥用,因为闭包会使得函数中的变量都被保存在内存中,内存消耗很大,如果滥用闭包,会导致内存泄漏。所以在不需要使用闭包的时候,要及时释放引用,比如将引用闭包的函数赋值为null,让垃圾回收机制可以回收对应的内存。

另外,闭包会保留外部函数的作用域,所以在闭包中访问变量的时候,需要注意变量的变化,避免因为变量被修改而出现不符合预期的结果。比如在循环中创建闭包的时候,如果没有正确处理作用域,很容易拿到循环结束之后的变量值,这时候就需要通过块级作用域或者立即执行函数来保存每次循环的变量状态。

JavaScript闭包函数作用域实际应用场景修改时间:2026-06-25 23:39:39

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