10分钟带你深入理解JavaScript的执行上下文和闭包机制

news2024/12/23 10:22:28

 🎬 岸边的风:个人主页

 🔥 个人专栏 :《 VUE 》 《 javaScript 》

⛺️ 生活的理想,就是为了理想的生活 !

在这里插入图片描述

目录

 📚 前言

📘 一. JavaScript中的闭包

📘 二. 执行上下文与闭包

📘 三. 闭包的应用场景

📖 1. 数据封装和私有性

📖 2. 模块化编程

📖 3. 回调函数和事件处理

📖 4. 缓存和记忆化

📘 四. 闭包的优缺点

📖 1. 内存消耗

📖 2.  内存泄漏

📖 3.  性能影响

  📚 写在最后 



 📚 前言

JavaScript中的闭包源于计算机科学中的一种理论概念,称为“λ演算”(Lambda Calculus)。λ演算是计算机科学的基础之一,1930年由Alonzo Church提出,它是一种用于描述计算过程的数学抽象模型,也是函数式编程语言的基础。

JavaScript中,闭包是函数和声明该函数的词法环境的组合。这个环境包含了闭包创建时所能访问的所有局部变量

理解闭包,需要理解JavaScript的特性和工作原理。JavaScript的函数在创建时,就确定了其操作的上下文环境,即词法作用域。这是因为JavaScript采用的是静态作用域,也叫词法作用域,函数的作用域在函数定义的时候就决定了。

例如:

function outer() {
    var name = 'JavaScript';
    function inner() {
        console.log(name);
    }
    return inner;
}

var innerFunc = outer();
innerFunc(); // 输出 'JavaScript'

在这个例子中,outer函数返回了inner函数inner函数访问了outer函数的局部变量name,因此形成了一个闭包。即使outer函数执行完毕,name变量的引用仍然被保留,因此innerFunc在执行时仍然能够输出 'JavaScript'。

闭包的概念虽然来自计算机科学的深层理论,但在日常的JavaScript编程中,它是一个非常实用且常见的特性,被广泛用于如数据隐藏和封装、模块化编程、回调函数和计时器等许多场景中。

📘 一. JavaScript中的闭包

JavaScript中,闭包是一个强大而复杂的特性,理解和利用好闭包对于编写高效且安全的代码至关重要。下面就让我们深入地了解一下JavaScript的闭包。

闭包是指那些能够访问自由变量的函数。什么是自由变量呢?如果一个变量在函数内部被引用,但它既不是函数的参数也不是函数的局部变量,那么就称之为“自由变量”。

例如,我们有一个外部函数和一个内部函数

function outerFunction(outerVariable) {
  function innerFunction() {
    console.log(outerVariable);
  }
  return innerFunction;
}

var inner = outerFunction('Hello Closure');
inner(); // 输出 'Hello Closure'

在这个例子中,outerFunction是一个外部函数,接受一个参数outerVariable。它包含一个内部函数innerFunction,这个内部函数没有自己的参数或局部变量,但却引用了外部函数的变量outerVariable。所以,我们说innerFunction是一个闭包,而outerVariable就是它的自由变量。

需要注意的是,由于JavaScript的垃圾回收机制,如果一个变量离开了它的作用域,那么这个变量就会被回收。但是,由于innerFunction是一个闭包,它引用了outerVariable,所以即使outerFunction执行完毕,outerVariable离开了它的作用域,但仍然不会被垃圾回收机制回收。

再者,每次调用外部函数,都会为内部的闭包创建一个新的作用域。例如:

var inner1 = outerFunction('Hello Closure 1');
var inner2 = outerFunction('Hello Closure 2');
inner1(); // 输出 'Hello Closure 1'
inner2(); // 输出 'Hello Closure 2'

这里,inner1inner2是两个不同的闭包。他们分别有自己的作用域,储存了不同的outerVariable

📘 二. 执行上下文与闭包

JavaScript中,执行上下文(execution context)是一个关键概念,与闭包(closure)密切相关。理解执行上下文如何与闭包交互可以帮助我们深入理解闭包的工作原理和行为。

执行上下文是JavaScript代码执行时的环境。它包含了变量、函数声明、作用域链等信息,用于管理和跟踪代码的执行过程。当一个函数被调用时,就会创建一个新的执行上下文。每个执行上下文都有自己的词法环境(Lexical Environment),用于存储变量和函数的声明。

在理解闭包之前,让我们先了解一下执行上下文的创建和销毁过程。当函数被调用时,会创建一个新的执行上下文,并将其推入执行上下文栈(execution context stack)中。当函数执行完毕后,其执行上下文会从栈中弹出并销毁

现在,让我们通过一个例子来更具体地了解执行上下文和闭包之间的关系:

function outerFunction(outerVariable) {
  function innerFunction(innerVariable) {
    console.log('outerVariable:', outerVariable);
    console.log('innerVariable:', innerVariable);
  }
  return innerFunction;
}

var newFunction = outerFunction('outside');
newFunction('inside'); // 输出: outerVariable: outside innerVariable: inside

在这个例子中,当调用outerFunction时,会创建一个新的执行上下文,其中包含了outerVariable参数和innerFunction函数声明。然后,outerFunction返回了innerFunction,并将其赋值给变量newFunction

现在让我们来看看闭包是如何形成的。当innerFunction被返回时,它会携带其词法环境(包含outerVariable)一起返回。这意味着innerFunction保持对outerVariable的引用,即使outerFunction执行完毕并且其执行上下文已经销毁。

这就是闭包的力量所在。它允许内部函数(innerFunction)访问其词法环境中的变量(outerVariable),即使这些变量在其创建时的执行上下文已经不存在。

在这个例子中,newFunction就是一个闭包。它引用了外部函数outerFunction的词法环境,其中包含了outerVariable变量。因此,当我们调用newFunction时,它可以访问并打印出outerVariableinnerVariable的值。

执行上下文和闭包的关系是密不可分的。闭包是由执行上下文中的变量引用形成的,而这些变量保留在闭包的作用域中。这使得闭包能够在函数执行完成后继续访问这些变量,实现了JavaScript中非常重要的特性。

理解执行上下文和闭包的交互对于编写复杂的JavaScript代码非常重要。它有助于我们更好地理解作用域、变量的生命周期以及如何正确使用闭包来解决问题。同时,它也帮助我们避免一些潜在的问题,如内存泄漏和不必要的资源消耗

📘 三. 闭包的应用场景

闭包在JavaScript中有广泛的应用场景,它是一种强大的编程工具,可以解决许多常见的问题。下面我们来介绍一些常见的闭包应用场景。

📖 1. 数据封装和私有性

闭包可以用于创建私有变量,将变量隐藏在函数作用域内部,从而实现数据的封装和私有性。通过闭包,我们可以控制变量的访问权限,只暴露需要暴露的接口。这种封装机制可以防止外部代码直接访问和修改内部数据,增加代码的安全性

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

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

在这个例子中,createCounter函数返回一个对象,该对象包含了三个闭包函数,分别用于增加计数、减少计数和获取计数值。通过闭包,我们可以将count变量隐藏在函数内部,并通过闭包函数来操作和访问这个变量。

📖 2. 模块化编程

闭包可以用于实现模块化编程,将相关的变量和函数组织在一个闭包内部,形成一个模块。这样可以避免全局命名冲突,提供命名空间,并且允许模块内部的函数相互调用和共享数据

var myModule = (function () {
  var privateVariable = '私有变量';

  function privateFunction() {
    console.log('私有函数');
  }

  return {
    publicMethod: function () {
      console.log(privateVariable);
    },
    publicFunction: function () {
      privateFunction();
    }
  };
})();

myModule.publicMethod(); // 输出: 私有变量
myModule.publicFunction(); // 输出: 私有函数

在这个例子中,我们使用了立即调用函数表达式(IIFE)来创建一个闭包,形成一个独立的模块。模块内部的变量和函数对外部是不可见的,只有通过公共接口才能访问。

📖 3. 回调函数和事件处理

闭包常常用于处理回调函数和事件处理,特别是在异步编程中。由于闭包的特性,它可以捕获外部函数的上下文,并在内部函数被调用时保留这个上下文,从而实现对异步操作的响应。

function fetchData(url, callback) {
  fetch(url).then(function (response) {
    return response.json();
  }).then(function (data) {
    callback(data);
  });
}

function processData(data) {
  console

.log(data);
}

fetchData('https://api.example.com/data', processData);

在这个例子中,fetchData函数通过闭包捕获了processData函数作为回调函数。当异步操作完成时,它会调用回调函数并传递数据给它。闭包保持了回调函数的上下文,使得回调函数可以访问外部的processData函数

📖 4. 缓存和记忆化

闭包还可以用于实现缓存和记忆化功能。通过闭包,我们可以在函数内部维护一个缓存,避免重复计算相同的结果,提高函数执行的性能

function memoizedFunction() {
  var cache = {};
  return function (arg) {
    if (cache[arg]) {
      return cache[arg];
    }
    // 计算结果
    var result = // ...
    cache[arg] = result;
    return result;
  };
}

var memoized = memoizedFunction();
console.log(memoized('value')); // 第一次计算并缓存结果
console.log(memoized('value')); // 直接从缓存中读取结果

在这个例子中,memoizedFunction返回一个闭包函数,用于记忆化计算结果。闭包内部维护了一个缓存对象cache,当输入相同的参数时,直接从缓存中读取结果,避免重复计算。

闭包在JavaScript中有许多其他的应用场景,如实现延迟执行、函数柯里化、实现迭代器等。了解闭包的应用场景可以帮助我们写出更加优雅、高效的代码,并利用闭包的强大能力解决问题

📘 四. 闭包的优缺点

当谈到闭包的缺点时,主要涉及内存消耗、内存泄漏和性能影响。下面是一些代码示例,帮助我们理解这些缺点。

📖 1. 内存消耗

闭包会导致内存占用增加,因为它们会保留对外部变量的引用,即使外部函数执行完毕。这可能会导致内存占用过高

function createHugeArray() {
  var arr = new Array(1000000).fill('Huge Data');
  return function() {
    console.log(arr.length);
  };
}

var bigDataFunc = createHugeArray();
bigDataFunc(); // 输出: 1000000

在这个例子中,createHugeArray函数返回一个闭包函数,它引用了一个巨大的数组arr。即使createHugeArray执行完毕,arr仍然被闭包引用,无法被垃圾回收机制回收,从而导致内存占用增加。

📖 2.  内存泄漏

由于闭包会持有对外部变量的引用,如果不正确地处理闭包的使用,可能会导致内存泄漏。如果一个闭包长时间存在,但不再需要,它会一直持有对外部变量的引用,使这些变量无法被垃圾回收

function leakMemory() {
  var data = 'Sensitive Data';
  var timer = setInterval(function() {
    console.log(data);
  }, 1000);
}

leakMemory();

在这个例子中,leakMemory函数创建了一个闭包,它引用了一个定时器内部的函数。即使leakMemory执行完毕,定时器仍然在持续执行,因此闭包会一直存在并引用data变量,导致data无法被垃圾回收。

📖 3.  性能影响

闭包可能对性能产生一定的影响,特别是在涉及大量变量或复杂词法环境的情况下。闭包的创建和执行可能消耗更多的时间和资源

function calculate() {
  var result = 0;
  for (var i = 0; i < 1000000; i++) {
    result += i;
  }
  return function() {
    console.log(result);
  };
}

var expensiveFunc = calculate();
expensiveFunc(); // 输出: 499999500000

在这个例子中,calculate函数返回一个闭包函数,它引用了一个在循环中计算的结果。由于闭包保留了这个结果,闭包的执行可能会耗费更多的时间和资源

为了减少闭包的缺点,我们可以采取以下措施:

  • 优化内存使用:在闭包中避免持有大量数据或不必要的引用。确保只保留必要的变量和引用。
  • 及时清理闭包:在不需要使用闭包时,手动解除对闭包的引用,以便垃圾回收机制可以回收闭包相关的资源。
  • 避免滥用闭包:只在必要的情况下使用闭包,避免在不必要的场景中使用闭包。
  • 优化性能:在闭包的创建和使用过程中,尽量避免不必要的计算或资源消耗,以提高性能

通过合理使用和处理闭包,我们可以最大限度地减少其缺点,同时享受闭包在JavaScript中带来的强大功能。

  📚 写在最后 

执行上下文由三个重要组成部分构成:

  1. 变量对象(Variable Object):存储变量、函数声明和函数参数。
  2. 作用域链(Scope Chain):决定了变量和函数的访问权限,通过一系列嵌套的作用域构成。
  3. this 值:指向当前执行上下文所属的对象。

闭包是指能够访问自己定义的词法作用域以及父级作用域中变量的函数。通过闭包,函数可以记住并访问其在定义时的词法环境。

闭包的特点:

  1. 闭包可以访问外部函数的变量和参数,即使外部函数已经执行完毕。
  2. 外部函数的变量和参数在闭包中被保留,并且可以在闭包中被修改。
  3. 闭包可以通过返回函数来创建,在外部函数之外对其进行引用和调用。

闭包在JavaScript中有多种应用场景,例如创建私有变量、封装模块、保存状态等。但需要注意闭包可能导致内存泄漏问题,因此需要合理使用和释放闭包。

深入理解JavaScript执行上下文和闭包机制对于理解函数作用域、变量生命周期以及如何有效地使用闭包非常重要。熟练掌握这些概念可以提高代码质量和开发效率

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1006157.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

03-Redis主从架构

上一篇&#xff1a;02-Redis持久化 1.主从架构搭建 配置从节点步骤&#xff1a; 1、复制一份redis.conf文件 2、将相关配置修改为如下值&#xff1a; port 6380 pidfile /var/run/redis_6380.pid # 把pid进程号写入pidfile配置的文件 logfile "6380.log" dir …

使用亚马逊云科技Amazon SageMaker,为营销活动制作广告素材

广告公司可以使用生成式人工智能和文字转图像根基模型&#xff0c;制作创新的广告素材和内容。在本篇文案中&#xff0c;将演示如何使用亚马逊云科技Amazon SageMaker从现有的基本图像生成新图像&#xff0c;这是一项完全托管式服务&#xff0c;用于大规模构建、训练和部署机器…

PPPoE适用于Linux操作系统的指南

欢迎来到本文&#xff0c;亲爱的Linux用户&#xff01;今天&#xff0c;我将为你提供一个完整的指南&#xff0c;教你如何在Linux操作系统上设置和配置PPPoE连接。无需担心复杂性&#xff0c;跟着我一步一步来&#xff0c;你将轻松掌握这一技能&#xff01; 第一步&#xff0c…

工厂除静电除尘设备--离子风枪

静电无处不在&#xff0c;区别在于静电的多少而已。特别是工业生产过程中&#xff0c;大量的静电会有很多危害。 静电的危害有几点&#xff1a;1.引起电子设备的故障或误动作&#xff0c;造成电磁干扰。2.击穿集成电路和精密的电子元件&#xff0c;或使元件老化&#xff0c;拉…

酷开科技打造更好体验服务用户

智能电视以其海量资源、智慧大屏、高清画质等特点在国内快速普及。然而&#xff0c;随着用户量的增加、用户群体的需求多元化&#xff0c;导致消费者对智能电视的应用要求越来越高&#xff0c;不仅希望智能电视内容丰富&#xff0c;最好还能拥有“多合一”的功能。 好在&#…

气传导蓝牙耳机什么牌子好?精选5款值得推荐的气传导耳机分享

​无论是运动爱好者、户外探险家还是上班族&#xff0c;气传导耳机都能带给您全新的聆听体验。但选择一款好的耳机&#xff0c;也不是一件容易的事&#xff0c;市面气传导耳机质量也参差不齐&#xff0c;有很多使用起来不好用。所以小篇从网上精选5款最值得推荐的气传导耳机&am…

个人云存储:使用Cpolar和极简主义文件管理器构建的公网访问平台

文章目录 1. 前言2.Tiny File Manager网站搭建2.1.Tiny file manager下载和安装2.2 Tiny file manager网页测试2.2 Tiny file manager网页测试3. 本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试总结 1. 前言 文件共享和查阅是现在网络最常见的应用场景&am…

搞懂 Prometheus 这四种指标类型,谁都可能成为监控老司机?

指标是用来衡量性能、消耗、效率和许多其他软件属性随时间的变化趋势。它们允许工程师通过警报和仪表盘来监控一系列测量值的演变&#xff08;如CPU或内存使用量、请求持续时间、延迟等&#xff09;。指标在IT监控领域有着悠久的历史&#xff0c;并被工程师广泛使用&#xff0c…

管理类联考——数学——汇总篇——知识点突破——应用题——至少至多

&#x1f434; ⛲️ 一、考点讲解 至少至多问题也属于动态的最值问题&#xff0c;是考生失分率较高的题型&#xff0c;这类题目思路比较灵活&#xff0c;无固定化的公式和结论&#xff0c;所以考生必须灵活处理。 二、考试解读 这类题目难度较大&#xff0c;属于拉分题目。对…

企业综合信息化,人力资源管理,培训考学管理,电子采购(源码系统)

前言&#xff1a; 随着现代信息技术的不断发展&#xff0c;企业综合信息化已成为一种必然趋势。企业综合信息化是指将信息技术与企业业务流程相结合&#xff0c;实现企业资源的优化配置和高效利用&#xff0c;提升企业的竞争力和生产力。在实现企业综合信息化的过程中&#xff…

页面解析之结构化数据

结构化的数据是最好处理&#xff0c;一般都是类似JSON格式的字符串&#xff0c;直接解析JSON数据&#xff0c;提取JSON的关键字段即可。 1、JSON JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式&#xff1b;适用于进行数据交互的场景&#xff0c;比如网站前台…

汇报一下日常健身和锻炼方面的进展

跑步app最终从“咕咚”和“悦跑圈”二选一&#xff0c;锁定到悦跑圈上了。 七月太热&#xff0c;配速下降&#xff0c;但还能玩出花样 八月中旬气温稍降&#xff0c;配速提升&#xff0c;拟合抛物线 截至发博日,实际连续跑步接近600天了 截至发博日&#xff0c;完成2023跑量96…

测评补单:Temu卖家的市场攻略,轻松吸引更多流量和转化!

拼多多通过广告和低价策略的做法迅速拿下了美国市场。在欧美市场取得初步成果后&#xff0c;Temu又踏步走向了其他海外市场。今年7月&#xff0c;Temu先后进入了日本、韩国两地&#xff0c;进军亚洲市场。8月底&#xff0c;Temu又再一次扩张&#xff0c;这次目的地是东南亚。其…

新加坡市场最全开发攻略

作为东盟经济的“发动机”&#xff01;新加坡是继纽约、伦敦、香港之后的第四大国际金融中心。据《2022年全球竞争力》报告显示&#xff0c;新加坡是亚洲地区排名最高的&#xff0c;位列全球三甲之一。 同时&#xff0c;新加坡作为东盟(ASEAN)的核心成员国&#xff0c;还是世贸…

QT QProgressBar控件 使用详解

本文详细的介绍了QProgressBar控件的各种操作&#xff0c;例如&#xff1a;新建界面、设置最大最小值、设置进度、返回进度最小值、返回进度最大值、返回当前进度值、重置进度默认值、返回进度条文本、设置进度条文本、设置显示进度条、设置垂直水平、样式表、信号槽、源代码、…

第2章_freeRTOS入门与工程实践之单片机程序设计模式

本教程基于韦东山百问网出的 DShanMCU-F103开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id724601559592 配套资料获取&#xff1a;https://rtos.100ask.net/zh/freeRTOS/DShanMCU-F103 freeRTOS系列教程之freeRTOS入…

Leetcode376. 摆动序列

Every day a Leetcode 题目来源&#xff1a;376. 摆动序列 解法1&#xff1a;动态规划 约定&#xff1a; 某个序列被称为「上升摆动序列」&#xff0c;当且仅当该序列是摆动序列&#xff0c;且最后一个元素呈上升趋势。某个序列被称为「下降摆动序列」&#xff0c;当且仅当…

再聊Java Stream的一些实战技能与注意点

大家好&#xff0c;又见面了。 在此前我的文章中&#xff0c;曾分2篇详细探讨了下JAVA中Stream流的相关操作&#xff0c;2篇文章收获了累计 10w阅读、2k点赞以及 5k收藏的记录。能够得到众多小伙伴的认可&#xff0c;是技术分享过程中最开心的事情。 不少小伙伴在评论中提出了…

前端开发工程师:职业前景、工资、 具体工作

该篇适用于从零基础学习前端的小白 一、职业前景 前端这两年也是非常火的&#xff0c;就业的前景也是非常不错的。 1.需求持续增长&#xff1a; 随着互联网和移动设备的普及&#xff0c;越来越多的企业和组织需要建立和维护网站、应用程序和在线平台。这导致了对具有前端开发…

Spring Cloud Eureka:服务注册与发现

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; Spring Cloud Eureka&#xff1a;服务注册与发现 Spring Cloud Eureka是Spring Cloud生态系统中的一个组件&#xff0c;它是用于实现服务注册与发现的服务治理组件。在…