首页 > 编程语言 >JavaScript中闭包详解+举例,闭包的各种实践场景:高级技巧与实用指南

JavaScript中闭包详解+举例,闭包的各种实践场景:高级技巧与实用指南

时间:2024-08-31 10:23:08浏览次数:10  
标签:闭包 function const 函数 中闭 JavaScript return 变量

目录

闭包的各种实践场景:高级技巧与实用指南

一、什么是闭包?

1、闭包的基本概念

2、闭包的工作原理

3、闭包的用途

二、闭包的实际应用场景

1、模拟私有变量

2、事件处理和回调函数

3、延迟函数和异步操作

4、柯里化

5、备忘录模式(Memoization)

三、闭包的性能问题

1、内存泄漏

四、最佳实践与建议

五、总结


作者:watermelo37

涉及领域:Vue、SpingBoot、Docker、LLM、python等

-----------------------------------------------------------------------------------

-------温柔地对待温柔的人,包容的三观就是最大的温柔。-------

-----------------------------------------------------------------------------------

闭包的各种实践场景:高级技巧与实用指南

        闭包在很多现代编程语言中都存在。常见支持闭包的语言有 JavaScript、Python、Ruby、Swift、Kotlin、Scala 等。本文将着重讲在JavaScript中闭包的常见用法及实操意义。

        在JavaScript中,闭包(Closure)是一个非常重要的概念,几乎贯穿于日常开发的方方面面。尽管它强大且用途广泛,但对于初学者而言,理解闭包的原理和实际应用常常是一个挑战。在这篇文章中,将通过理论与实践相结合的方式,帮助你全面理解闭包的本质、原理及其在实际项目中的应用。

一、什么是闭包?

1、闭包的基本概念

        闭包可以简单理解为:一个函数和其词法环境的组合。当一个函数能够记住并访问其词法作用域,即使函数在其词法作用域之外执行,这个函数就被称为闭包。

        在JavaScript中,函数在创建时会形成一个包含函数内部变量和外部环境的闭包。这意味着,闭包可以“记住”其创建时的上下文,并能在稍后调用时访问这些变量。

2、闭包的工作原理

        要理解闭包的工作原理,我们首先需要理解JavaScript的执行上下文和作用域链。当一个函数在另一个函数内部被定义时,它会包含对外部函数变量的引用。这些引用在外部函数执行完毕后不会被销毁,而是被闭包所保留。

function outerFunction() {
    let outerVariable = 'I am from outer scope';
    function innerFunction() {
        console.log(outerVariable);
    }
    return innerFunction;
}

const closure = outerFunction();
closure();  // 输出: I am from outer scope

        在上面的例子中,innerFunction保留了对outerVariable的访问权,即使outerFunction已经执行完毕。这就是闭包的基本特性。

3、闭包的用途

  • 数据封装和私有化(模拟私有变量)
  • 维持变量初始状态
  • 柯里化(Currying)
  • 函数工厂(创建函数的函数)

二、闭包的实际应用场景

        闭包不仅仅是一个理论概念,在实际开发中它有很多重要的应用场景。

1、模拟私有变量

        JavaScript中没有私有变量的概念,但通过闭包可以模拟出类似私有变量的效果。

function createCounter() {
    let count = 0;
    return {
        increment: function() {
            count++;
            return count;
        },
        decrement: function() {
            count--;
            return count;
        }
    };
}

const counter = createCounter();
console.log(counter.increment());  // 输出: 1
console.log(counter.increment());  // 输出: 2
console.log(counter.decrement());  // 输出: 1

        在这个例子中,count变量无法直接从外部访问,只能通过increment和decrement方法进行操作。这种方式在编写模块化代码时尤为有用。

2、事件处理和回调函数

        闭包在事件处理和回调函数中非常常见,尤其是在需要保持状态或访问外部变量的情况下。

function setupClickHandlers() {
    const buttons = document.querySelectorAll('button');
    for (let i = 0; i < buttons.length; i++) {
        buttons[i].addEventListener('click', function() {
            console.log('Button ' + i + ' clicked');
        });
    }
}

setupClickHandlers();

        每个按钮点击时,都会输出其对应的索引。这是因为闭包“记住”了当时的 i 值。

3、延迟函数和异步操作

        闭包在处理延迟函数或异步操作时也非常有用。

for (var i = 1; i <= 5; i++) {
    setTimeout(function() {
        console.log(i);
    }, i * 1000);
}

// 通过闭包解决变量提升问题
for (var i = 1; i <= 5; i++) {
    (function(j) {
        setTimeout(function() {
            console.log(j);
        }, j * 1000);
    })(i);
}

        在第一个例子中,所有的setTimeout函数都会输出5,因为它们共享了同一个i变量。而在第二个例子中,通过闭包创建了新的作用域,每个setTimeout函数都有自己的j变量,从而正确输出1到5。

4、柯里化

        柯里化(Currying)是一种将接受多个参数的函数转换成一系列接受单个参数的函数的技术。这种技术以数学家哈斯凯尔·柯里(Haskell Curry)的名字命名。在柯里化过程中,原函数被转换成一个函数,这个函数接受一个参数并返回一个新的函数,后者再次接受下一个参数,依此类推,直到收集完所有需要的参数。

function add(x) {
  return function(y) {
    return x + y;
  };
}

const addFive = add(5);
console.log(addFive(3)); // 输出 8

        在这个例子中,add 函数返回一个新的函数,它记住了 x 的值,并接受另一个参数 y 来完成加法。

5、备忘录模式(Memoization)

        备忘录模式特别适用于递归算法,如计算斐波那契数列,或者任何重复调用相同参数的递归函数。以下是使用闭包实现备忘录模式的一个例子。

function memoize(fn) {
  const cache = {};
  return function(...args) {
    const key = args.toString(); // 将参数列表转换为字符串作为缓存键
    if (key in cache) {
      return cache[key];
    } else {
      const result = fn.apply(this, args);
      cache[key] = result;
      return result;
    }
  };
}

const fibonacci = (n) => {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
};

const memoizedFibonacci = memoize(fibonacci);

console.log(memoizedFibonacci(10)); // 使用备忘录快速计算

        在这个例子中,memoize 函数是一个高阶函数,它接收一个函数 fn 作为参数,并返回一个新的函数。新函数会检查传入的参数是否已经在缓存 cache 中,如果是,则直接返回缓存的结果;如果不是,则调用原始函数 fn,将结果存入缓存,并返回结果。

三、闭包的性能问题

        虽然闭包功能强大,但滥用闭包可能会带来性能问题。每次创建闭包时,都会在内存中保留一份变量的引用,过多的闭包可能导致内存泄漏。因此,在编写代码时应谨慎使用闭包,避免在不必要的场合创建闭包。

1、内存泄漏

        不当的闭包使用会导致内存泄漏,特别是在循环中创建大量闭包时。如果闭包引用了不再需要的变量或对象,内存将无法释放。

function leakyFunction() {
    let largeData = new Array(1000000).join('*');
    return function() {
        console.log(largeData.length);
    };
}

const leaky = leakyFunction();
// largeData 仍然保存在内存中,即使它不再被需要

        在上述例子中,largeData占用了大量内存,即使在函数执行完毕后也不会被释放。因此,尽量避免在闭包中引用不必要的变量。

四、最佳实践与建议

        为了更好地使用闭包,以下是一些最佳实践和建议:

  1. 控制闭包的数量:不要在不必要的场合创建闭包,尤其是在高频调用的函数中。

  2. 释放不再需要的变量:如果闭包引用了大量数据,应当在适当时候释放这些数据,以避免内存泄漏。

  3. 使用let或const代替var:在使用闭包时,let或const声明的变量具有块级作用域,能更好地避免变量提升问题。

  4. 理解作用域链:深入理解JavaScript的作用域链和执行上下文,有助于更好地掌握闭包的使用。

五、总结

        闭包是JavaScript中不可或缺的部分,它不仅可以增强代码的可维护性,还能在模块化、回调处理等场景中发挥巨大作用。然而,闭包的强大也意味着需要谨慎使用,避免潜在的性能问题和内存泄漏。通过对闭包原理的深入理解以及在实际项目中的灵活应用,你将能够更加高效地编写出简洁且功能强大的代码。

        只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

        更多优质内容,请关注:

        你真的会使用Vue3的onMounted钩子函数吗?Vue3中onMounted的用法详解

        通过array.filter()实现数组的数据筛选、数据清洗和链式调用

        el-table实现动态数据的实时排序,一篇文章讲清楚elementui的表格排序功能

        极致的灵活度满足工程美学:用Vue Flow绘制一个完美流程图

        shpfile转GeoJSON且控制转化精度;如何获取GeoJSON?GeoJson结构详解

        Docker 入门全攻略:安装、操作与常用命令指南

        通过array.reduce()实现数据汇总、条件筛选和映射、对象属性的扁平化、转换数据格式等

        巧用Array.forEach:简化循环与增强代码可读性

        Mapbox添加行政区矢量图层、分级设色图层、自定义鼠标悬浮框、添加天地图底图等

        管理数据必备!侦听器watch用法详解

标签:闭包,function,const,函数,中闭,JavaScript,return,变量
From: https://blog.csdn.net/RenGJ010617/article/details/141439236

相关文章

  • JavaScript - 闭包
    使用场景数据封装闭包允许创建私有变量,这些变量在函数外部无法直接访问。通过闭包,可以创建具有私有状态的对象,从而实现数据封装。例如:functioncreateCounter(){letcount=0;//count是私有变量returnfunction(){count++;returncount;};}const......
  • javascript 检测 麦克风状态
    <htmllang="zh"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><title>麦克风监听示例<style>body......
  • JavaScript 的模板字符串
    字符串插值JavaScript中使用反引号`包裹的字符串叫模板字符串(templateliterals)。人们常用它拼接变量和字符串,即所谓的字符串插值(stringinterpolation)。在使用字符串插值时,使用${}包裹变量或表达式,它是变量的占位符。多行文本模板字符串支持多行文本(multi-linestr......
  • GEE中用JavaScript计算遥感数据在两个日期中变化值的多年平均
      本文介绍在谷歌地球引擎GEE中,提取、计算某一种遥感影像产品在连续的多年中,2个不同时相的数据差值的多年平均值,并将计算得到的这一景差值的结果图像导出的方法。  首先,我们来看一下本文需要实现的需求。现在我们希望计算某一个地区中,在2013年到2020年的这8年中,第257天与249天......
  • JavaScript开发学习札记:一位Java后端程序员的成长之路
    前言:        这是一篇关于JavaScript的学习笔记,目的是针对java后端开发人员,快速入门并掌握JavaScript这门语言的基本使用,并且能够进入下一阶段框架的学习。引言:为什么学习JavaScript?JavaScript是web开发人员必须学习的3门语言中的一门:HTML定义了网页的内......
  • JavaScript高阶 day-04
    目录一.严格模式1.1什么是严格模式1.2开启严格模式1.3严格模式中的变化二.高阶函数三.闭包3.1变量的作用域3.2什么是闭包3.3闭包的作用3.4闭包的案例四.递归4.1什么是递归4.2利用递归求1~n的阶乘五.赋值(地址传递)-浅拷贝-深拷贝5.1地址传递5.2Object.......
  • JavaScript高阶 day-03
    目录一.函数内部的this指向二.函数对象的原型的方法2.1Function.prototype.call()---call()方法2.2Function.prototype.apply()---apply()方法2.3Function.prototype.bind()---bind()方法2.4call、apply、bind三者的异同2.5Object.prototype.toString()判断数据类......
  • Javascript里的闭包
    闭包闭包,允许函数访问创建时的作用域中的变量,即使该函数在其原始作用域之外被执行。这使得闭包非常适合用于封装私有变量和创建具有特定状态的函数。Javascript里的闭包functioncreateCounter(){letcount=0;//计数器的私有变量return{increment:f......
  • JavaScript
    一,js中的数据类型1数值型:number整数和浮点数统称为数值。例如85或3.1415926等。2字符串型:String由0个,1个或多个字符组成的序列。在JavaScript中,用双引号或单引号括起来表示,如"您好"、'学习JavaScript'等。3逻辑(布尔)型:boolean用true或false来表示。4空(null)值:表示没有......
  • JavaScript变量和数据类型
    一、变量1、简介​在程序运行过程中其值可以改变的量,是一个数据存储空间的表示,即给内存空间起别名​将数值通过变量进行存储,以便程序随时使用,通俗点来说,变量就是用来存储数据的​JavaScript是弱变量类型的语言,声明变量只需要使用var关键字​语法:var变量名=变量值;<s......