JavaScript函数闭包解析

一、什么是闭包

JavaScript中的函数闭包是指函数可以访问其父级作用域中的变量,即使函数在父级作用域外被调用。闭包可以获取和修改其父级作用域中的变量,即使父级作用域已经被销毁。

在JavaScript中,当一个函数被定义时,它会创建一个闭包。闭包包含函数中使用的任何变量,并允许函数在父级作用域中访问这些变量。

闭包的一个常见用例是创建私有变量。通过使用闭包,在函数内部定义的变量可以在函数外部是不可访问的,从而实现了封装和信息隐藏。

以下是一个简单的例子,展示了如何使用闭包创建一个私有计数器:

function createCounter() {
  let count = 0;

  function increment() {
    count++;
    console.log(count);
  }

  return increment;
}

const counter = createCounter();
counter(); // 输出: 1
counter(); // 输出: 2

在上面的例子中,createCounter函数返回了一个内部函数incrementincrement函数可以访问并修改createCounter函数中的count变量,即使createCounter函数已经执行完毕。每次调用counter函数时,count的值会增加并被打印出来。

这是因为increment函数形成了一个闭包,它可以访问其父级作用域中的变量。在这个例子中,count变量就是一个闭包变量。

二、观察闭包

在JavaScript中,可以通过观察函数闭包来了解函数的作用域和变量的生命周期。以下是一些观察函数闭包的方法:

1.使用console.log()输出函数闭包:可以在函数内部使用console.log()输出函数内的变量,从而观察函数闭包。例如:

function outer() {
  var outerVar = "I'm in outer function";

  function inner() {
    var innerVar = "I'm in inner function";
    console.log(outerVar);
    console.log(innerVar);
  }

  return inner;
}

var innerFunc = outer();
innerFunc(); // 输出 "I'm in outer function" 和 "I'm in inner function"

2.使用debugger调试函数闭包:可以在函数内部使用debugger关键字设置断点,然后使用开发者工具的调试功能观察函数闭包的变量。例如:

function outer() {
  var outerVar = "I'm in outer function";

  function inner() {
    var innerVar = "I'm in inner function";
    debugger; // 设置断点
    console.log(outerVar);
    console.log(innerVar);
  }

  return inner;
}

var innerFunc = outer();
innerFunc(); // 在开发者工具中观察函数闭包的变量

3.使用闭包返回函数的属性值:可以通过闭包将函数内的变量返回为函数属性,然后在外部访问该函数属性。例如:

function outer() {
  var outerVar = "I'm in outer function";

  function inner() {
    var innerVar = "I'm in inner function";
    console.log(outerVar);
    console.log(innerVar);
  }

  inner.outerVar = outerVar; // 通过闭包将outerVar返回为inner函数的属性

  return inner;
}

var innerFunc = outer();
console.log(innerFunc.outerVar); // 输出 "I'm in outer function"

三、闭包的用途

闭包是JavaScript中非常有用的一种特性,它的主要用途有以下几个方面:

  1. 封装变量:闭包可以将变量封装起来,使其在函数外部无法直接访问,只能通过闭包内部的函数来访问和修改。这样可以保护变量的安全性,避免其他代码的误操作。

  2. 实现私有变量和私有方法:由于闭包的封装性,可以利用闭包实现类似于面向对象中的私有变量和私有方法的效果。外部无法直接访问和修改闭包内部的变量和方法,只能通过闭包内部提供的公共接口来操作。

  3. 记忆上下文:闭包可以记住其创建时的上下文环境,即使创建闭包的函数已经执行完毕,闭包仍然可以访问到当时的变量和参数。这种特性可以用于实现一些需要记住状态的功能,比如计数器、缓存等。

  4. 实现函数柯里化:柯里化(Currying)是一种将多个参数的函数转换为一系列单参数函数的技术。通过闭包,可以实现函数柯里化,将函数的部分参数先传入,返回一个新的函数,后续再传入其他参数。这样可以方便地复用函数和传入不同的参数组合。

  5. 模块化开发:闭包可以用于实现模块化开发,在一个函数内部定义多个变量和方法,并将其返回,供外部作为一个整体使用。这样可以避免全局命名冲突,提高代码的可维护性和重用性。

闭包在JavaScript中的用途是非常广泛的,可以实现很多有意思的功能和技巧。但是过度使用闭包可能会导致内存泄漏的问题,所以在使用闭包时需要注意内存管理。

四、闭包代码示例

下面是几个使用闭包的示例代码:

1.封装变量:

function createCounter() {
  let count = 0;
  
  return function() {
    return ++count;
  }
}

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

2.实现私有变量和私有方法:

function createPerson() {
  let name = "John";
  
  function changeName(newName) {
    name = newName;
  }
  
  function getName() {
    return name;
  }
  
  return {
    changeName,
    getName
  };
}

const person = createPerson();
console.log(person.getName()); // 输出:John
person.changeName("Alice");
console.log(person.getName()); // 输出:Alice
console.log(person.name); // 输出:undefined

3.记忆上下文:

function createCalculator() {
  let result = 0;
  
  return {
    add: function(num) {
      result += num;
    },
    subtract: function(num) {
      result -= num;
    },
    getResult: function() {
      return result;
    }
  };
}

const calculator = createCalculator();
calculator.add(5);
calculator.subtract(3);
console.log(calculator.getResult()); // 输出:2

4.实现函数柯里化:

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

const add5 = add(5);
console.log(add5(3)); // 输出:8
console.log(add5(7)); // 输出:12

5.模块化开发:

const module = (function() {
  let privateData = "Private";

  function privateMethod() {
    console.log("Private method");
  }

  return {
    publicMethod: function() {
      console.log("Public method");
    },
    getPrivateData: function() {
      return privateData;
    }
  };
})();

module.publicMethod(); // 输出:Public method
console.log(module.getPrivateData()); // 输出:Private
console.log(module.privateData); // 输出:undefined

这些示例代码展示了闭包的不同用途,通过使用闭包可以实现封装、保护、记忆状态、柯里化和模块化等功能。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows如何安装并启动Nginx

0、前言 Nginx 是一款高性能、轻量级的Web服务器和反向代理服务器,广泛应用于互联网领域。它以其高效稳定、内存占用少和丰富的模块化设计而受到开发者们的青睐。 在实际使用过程中,我们多数时候会在Linux系统上运行Nginx,但实际上&#xff…

国产强大免费WAF, 社区版雷池动态防护介绍

雷池WAF,基于智能语义分析的下一代 Web 应用防火墙 使用情况 我司于2023年4月23日对雷池进行测试,测试一个月后,于2023年5月24日对雷池进行正式切换,此时版本为1.5.1。 里程碑纪念 后续一直跟随雷池进行版本升级,当前…

秋招力扣刷题——从前序与中序遍历序列构造二叉树

一、题目要求 给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。 二、解法思路 根据二叉树的遍历结构重构二叉树,至少两种遍历方式结合&…

kettle生成uuid32位——kettle开发44

生成UUID: UUID是由一组字符组成,通常呈现为32位的十六进制数, 如 "550e8400-e29b-41d4-a716-446655440000" 目标: 生成的UUID是34位的,我们去掉-,转换为正常的32位 实现:

Android TextView的属性与用法

文本控件包括TextView、EditText、AutoCompleteTextView、CheckedTextView、MultiAutoCompleteTextView、TextInputLayout等,其中TextView、EditText是最基本最重要的文本控件,是必须要掌握的文本控件。 1.TextView TextView控件用于显示文本信息&…

Three.js机器人与星系动态场景(二):强化三维空间认识

在上篇博客中介绍了如何快速利用react搭建three.js平台,并实现3D模型的可视化。本文将在上一篇的基础上强化坐标系的概念。引入AxesHelper辅助工具及文本绘制工具,带你快速理解camer、坐标系、position、可视区域。 Three.js机器人与星系动态场景&#x…

红酒的秘密花园:探索葡萄的种植艺术

在远离城市喧嚣的某个角落,隐藏着一座神秘的红酒秘密花园。这里,葡萄藤缠绵交织,绿叶间闪烁着晶莹的露珠,仿佛在诉说着关于红酒与葡萄种植艺术的古老传说。今天,就让我们一起走进这片神秘的花园,探寻葡萄种…

Mysql查询IFNULL和想象的不一样

select sum(ifnull(a,0)) aaa,ifnull(sum(a),0) bbb from (select g.goodsid a from goods g where g.goodsid 601 ) tmp #注意 goodsid 601 的不存在 ​​​ 返回的结果和想象中不同,解释如下 在您SQL查询中,创建了一个子查询(别名为tmp&a…

FEBLESS SAP软件对芯片设计企业的重要性

在集成电路(IC)设计行业,无晶圆厂模式(Fabless)企业专注于芯片的设计和销售,而将制造和封装测试外包给专业的代工厂和封测厂。Fabless模式下,企业面临着复杂的供应链管理和项目协同挑战,而SAP软件作为一款成熟的企业资源规划(ERP)…

iiiiiiiiiiiiiiiiiiiiiiiiiio_contexttttttttttttttttttttttttt

https://www.cnblogs.com/bwbfight/p/17594353.html 谈一谈linux下线程池 - 白伟碧一些小心得 - 博客园 (cnblogs.com) 谈一谈linux下线程池 - 白伟碧一些小心得 - 博客园 (cnblogs.com) https://www.cnblogs.com/bwbfight/p/10901574.html 前面的设计,我们对asio…

Kafka集群安装部署

简介 Kafka是一款分布式的、去中心化的、高吞吐低延迟、订阅模式的消息队列系统。 同RabbitMQ一样,Kafka也是消息队列。不过RabbitMQ多用于后端系统,因其更加专注于消息的延迟和容错。 Kafka多用于大数据体系,因其更加专注于数据的吞吐能力…

AI网络爬虫006:从当当网批量获取图书信息

文章目录 一、目标二、输入内容三、输出内容一、目标 用户输入一个图书名称,然后程序自动从当当网批量获取图书信息 查看相关元素在源代码中的位置: 二、输入内容 第一步:在deepseek中输入提示词: 你是一个Python爬虫专家,一步步的思考,完成以下网页爬取的Python脚本任…

WEB攻防-XSS跨站反射型存储型DOM型标签闭合输入输出JS代码解析

文章目录 XSS跨站-输入输出-原理&分类&闭合XSS跨站-分类测试-反射&存储&DOM反射型XSS存储型XSSDOM-base型XSS XSS跨站-输入输出-原理&分类&闭合 漏洞原理:接受输入数据,输出显示数据后解析执行 基础类型:反射(非持续…

ffmpeg下载/配置环境/测试

一、下载 1、访问FFmpeg官方网站下载页面:FFmpeg Download Page; 2、选择适合Windows的版本(将鼠标移动到windows端)。通常,你会找到“Windows builds from gyan.dev”或者“BtbN GitHub Releases”等选项&#xff0…

Java的异常处理体系

目录 异常处理1、Java的异常类层次结构2、try-catch-finally 使用注意事项3、在Web应用中如何实现全局异常处理机制 异常处理 1、Java的异常类层次结构 其中Error表示程序运行错误 常见的错误类型有: OutOfMemoryError (内存溢出错误) StackOverFlowError (栈内存溢…

ctfshow-web入门-命令执行(web118详解)Linux 内置变量与Bash切片

输入数字和小写字母,回显 evil input 查看源码,发现这里会将提交的参数 code 传给 system 函数 使用 burpsuite 抓包进行单个字符的模糊测试 fuzz: 发现过滤掉了数字和小写字母以及一些符号,下面框起来的部分是可用的 结合题目提…

vue2使用use注册自定义指令实现输入控制与快捷复制

使用场景 在一些form表单填写内容的时候,要限制输入的内容必须是数值、浮点型,本来el-input-number就可以实现,但是它本身带那个数值控制操作,等一系列感觉不舒服的地方。如果只是使用el-input该多好,只要监听一下输入…

爬虫笔记20——票星球抢票脚本的实现

以下内容仅供交流学习使用!!! 思路分析 前面的爬虫笔记一步一步走过来我们的技术水平也有了较大的提升了,现在我们来进行一下票星球抢票实战项目,实现票星球的自动抢票。 我们打开票星球的移动端页面,分…

身份证OCR识别的深度解读

引言 随着信息技术的飞速发展,光学字符识别(OCR)技术在各个领域得到了广泛应用。身份证OCR识别,作为OCR技术的一个重要分支,以其高效、准确的特点,在身份验证、信息录入等方面发挥着重要作用。本文将深入解…

【Linux】Linux用户,用户组,其他人

1.文件拥有者 初次接触Linux的朋友大概会觉得很怪异,怎么“Linux有这么多用户,还分什么用户组,有什用呢?”,这个“用户与用户组”的功能可是相当健全而且好用的一个安全防护措施。 怎么说呢?由于Linux是个…