JavaScript中闭包的4个有用技巧

前言

当谈到JavaScript编程中的高级概念和技巧时,闭包(Closures)是一个重要而有趣的主题。闭包是一种函数与其创建时的词法环境的组合,它允许我们捕获和保留局部变量,并在函数之外使用它们。在这篇文章中,我们将深入探讨JavaScript中闭包的4种有用技巧,以及如何应用它们来解决各种问题和提高代码质量。

1. 解决循环中的问题

JavaScript中,循环中的变量作用域问题经常会导致预期之外的结果。通常,使用var声明变量会导致循环中的变量共享相同的作用域,因此在异步操作中,这些变量可能会具有意外的值。

问题场景:

css 复制代码
for (var i = 0; i < 3; i++) {
    setTimeout(() => {
        console.log(i); // 输出什么?
    }, 1000 * i);
}

上述代码将在1秒、2秒和3秒后分别打印出3,三次都是相同的值。这是因为setTimeout是异步的,它在循环结束后才执行,此时i的值已经是3

解决方法:

使用闭包来保存每次迭代中的i的值:

javascript 复制代码
for (var i = 0; i < 3; i++) {
    ((n) => {
        setTimeout(() => {
            console.log(n); // 输出0、1、2
        }, 1000 * n);
    })(i);
}

或者,更简单的方式是使用let来声明循环变量,它将在每次迭代中创建一个新的作用域:

css 复制代码
for (let i = 0; i < 3; i++) {
    setTimeout(() => {
        console.log(i); // 输出0、1、2
    }, 1000 * i);
}

2种方法都可以解决循环中的作用域问题。

2. 保存函数状态

闭包还可以用于实现记忆功能,通过缓存计算结果来提高性能。这在需要重复计算的函数中特别有用。

问题场景:

实现一个累加器:

ini 复制代码
let sum = 1;

function add(num) {
    sum += num;
    return sum;
}

console.log(add(1));
console.log(add(5));

每次调用add时,它都会将上次的值保存下来。但是这段代码有潜在的问题,那就是sum可能会被其他部分代码无意中修改。那如何解决?

解决方法:

使用闭包就可以规避上面存在的问题且可以缓存已经计算的值:

javascript 复制代码
function calculator(val) {
    let sum = val;
    return function(num) {
        sum += num;
        return sum;
    }
}
const add = calculator(1);
console.log(add(1)); //2
console.log(add(5)); //7

每次调用返回的函数时,它都会将传递给它的数字加到总和中,并返回新的总和。

3. 封装私有变量和属性

在过去,为了保护对象的私有变量,常常使用闭包。通过闭包,可以将变量封装在函数内部,只能通过函数暴露的接口来访问和修改。

问题场景:

scss 复制代码
function add() {
    let count = 0;
    count++;
    console.log(count);
}
add(); //输出1
add(); //输出1
add(); //输出1

调用函数,输出的结果都是1,但是显然我们想要的效果是让count每次加1的。那如何解决呢?

解决方法:

使用闭包来封装私有变量:

scss 复制代码
function add(){
    let count = 0;
    function a() {
        count++;
        console.log(count);
    }
    return a;
}
var res = add();
res() //1
res() //2
res() //3

add函数返回了一个闭包a,其中包含了count变量。由于count只在add函数内部定义,因此外部无法直接访问它。但是,由于a函数引用了count变量,因此count变量的值可以在闭包内部被修改和访问,这样就可以防止它被恶意修改了。

4. 函数柯里化

函数柯里化是一种将接受多个参数的函数转化为一系列接受一个参数的函数的过程。这也可以通过闭包来实现。

问题场景:

csharp 复制代码
const add = (a, b, c) => {
    return a + b + c;
}

console.log(add(2, 3, 4)); // 输出9

解决方法:

使用闭包来实现函数柯里化:

scss 复制代码
function curry(callback) {
    const args = [];

    return function curried(...newArgs) {
        args.push(...newArgs);

        if (args.length >= callback.length) {
            return callback(...args);
        } else {
            return curried;
        }
    };
}

function add(a, b, c) {
    return a + b + c;
}

const curriedAdd = curry(add);

console.log(curriedAdd(2)(3)(4)); // 输出 9

函数柯里化使函数更加灵活,能够逐步接受参数,提高代码的可重用性和可读性。

总结

在本文中,我们深入探讨了JavaScript中闭包的4种有用技巧,以及如何应用它们来解决各种问题和提高代码质量。这些技巧包括解决循环中的变量作用域问题,实现记忆功能以提高性能,封装私有变量和属性,以及使用函数柯里化来提高函数的灵活性。

闭包是JavaScript中一个强大的概念,它允许我们在函数之外访问和操作局部变量,从而解决了许多常见的编程问题。虽然闭包在JavaScript中有着广泛的应用,但也需要小心使用,以避免潜在的内存泄漏问题。确保在不再需要闭包时,及时释放对其的引用,以帮助垃圾回收器正常运作。希望本文对你理解JavaScript中的闭包和如何应用它们有所帮助。

后语

小伙伴们,如果觉得本文对你有些许帮助,点个👍或者➕个关注再走吧^_^ 。另外如果本文章有问题或有不理解的部分,欢迎大家在评论区评论指出,我们一起讨论共勉。

相关推荐
腾讯TNTWeb前端团队1 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰5 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪5 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪5 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy6 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom6 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom6 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom6 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom7 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom7 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试