### JavaScript中的闭包详解 #### 一、闭包的基本概念 闭包是JavaScript中一个重要的概念,也是理解和掌握高阶编程技巧的关键。根据司徒正美的介绍,闭包的定义可以概括为:“闭包就是在另一个作用域中保存了一份它从上一级函数或作用域取得的变量(键值对),而这些键值对是不会随上一级函数的执行完成而销毁的。” 这个定义虽然简洁,但对于初学者来说可能并不直观。 从技术层面讲,闭包涉及到两个主要概念:作用域链和变量的作用域。在JavaScript中,每个函数都有自己的作用域链,它决定了函数内部能够访问哪些变量。当一个函数被创建时,它会捕获其外部环境中的变量,并且即使外部函数已经执行完毕,这些变量仍然可以在闭包中被访问。 #### 二、闭包的特点与应用场景 1. **作为函数变量的一个引用**:当函数返回时,闭包保持对其所处作用域中变量的引用,即使这些变量原本应当随着函数执行结束而被销毁。这种特性使得闭包非常适合用于创建私有变量和方法。 2. **一个未释放资源的栈区**:闭包能够保持对外部变量的引用,因此这些变量所占用的内存不会被释放,直到包含闭包的函数完全退出执行环境。 #### 三、闭包的常见实现方式 闭包可以通过多种方式实现,下面列举了几种常见的实现形式: 1. **对象闭包**:通过`with`语句实现。例如: ```javascript var obj = { x: 1 }; with (obj) { console.log(x); // 输出 1 } ``` 这种方式虽然简洁,但由于`with`语句可能导致代码可读性和性能问题,在ES6之后并不推荐使用。 2. **函数闭包**:最常见的闭包形式。例如: ```javascript function outer() { var x = 1; return function inner() { console.log(x); // 输出 1 }; } var closure = outer(); closure(); // 输出 1 ``` 这里`inner`函数构成了一个闭包,它可以访问`outer`函数中的变量`x`。 3. **异常处理闭包**:通过`try...catch`语句实现。例如: ```javascript try { // 异常代码 } catch (e) { // 处理异常 } ``` 在某些情况下,`catch`块也可以看作是一种闭包的形式,尽管这种形式并不常用,且在IE浏览器中可能存在兼容性问题。 #### 四、闭包的实际应用案例 1. **生成唯一ID**: ```javascript var uniqueID = (function () { var id = 0; return function () { return id++; }; })(); console.log(uniqueID()); // 输出 0 console.log(uniqueID()); // 输出 1 ``` 上述代码展示了如何使用闭包来生成全局唯一的ID。每次调用`uniqueID()`时都会返回一个新的递增数值。 2. **实现阶乘计算**: ```javascript var factorial = (function (n) { if (n < 1) { alert("invalid arguments"); return 0; } if (n == 1) { return 1; } else { return n * arguments.callee(n - 1); } })(4); console.log(factorial); // 输出 24 ``` 通过闭包实现递归函数,计算给定数字的阶乘。 3. **封装对象属性的访问器**: ```javascript function User(properties) { var objThis = this; for (var i in properties) { (function () { var t = properties[i]; objThis["get" + i] = function () { return t; }; objThis["set" + i] = function (val) { t = val; }; })(); } } var user = new User({ name: "Bob", age: 44 }); console.log(user.getname()); // 输出 Bob console.log(user.getage()); // 输出 44 user.setname("Mike"); console.log(user.getname()); // 输出 Mike user.setage(22); console.log(user.getage()); // 输出 22 ``` 这个例子展示了如何使用闭包来实现对象属性的getter和setter方法,确保了数据的安全性和封装性。 #### 五、解决实际问题 最后来看一个具体的实例问题:如何使三个`<li>`元素的点击事件能正确弹出各自的索引值? ```html <ul> <li id="a1">aa</li> <li id="a2">aa</li> <li id="a3">aa</li> </ul> <script type="text/javascript"> for (var i = 1; i < 4; i++) { var id = document.getElementById("a" + i); (function (i) { id.onclick = function () { alert(i); // 正确显示 1, 2, 3 }; })(i); } </script> ``` 在这个示例中,通过立即执行函数表达式(IIFE)创建了一个闭包,使得每次循环时都会为每个`<li>`元素绑定一个独立的点击事件处理器。这样,每个处理器都能够访问到对应的`i`值,而不是循环结束后`i`的最终值。这种方法解决了闭包中的经典陷阱,即所有事件处理器都引用同一个变量的问题。 通过以上分析可以看出,闭包是JavaScript语言中一个强大且实用的功能,掌握它可以帮助开发者编写更加灵活和高效的代码。
- 粉丝: 3
- 资源: 945
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- 磁共振测试机工程图机械结构设计图纸和其它技术资料和技术方案非常好100%好用.zip
- pyqt编写界面,打开笔记本摄像头,支持缩放拖拽,并标记位置
- UI页面布局分析(5)- 评分弹窗的实现
- CentOS7与欧拉系统中达梦8数据库安装手册
- 1、C++SOCKET同步阻塞、异步非阻塞通信服务端、客户端代码,支持多个客户端连接 2、断线重连(服务端或客户端没有启动顺序要求,先开启的等待另一端连接); 3、服务端支持同时连接多个客户端;
- VMware虚拟机安装指南:下载、配置与启动操作详解
- gamebox.h-C++头文件,1.0版本
- 使用mysql存储过程和触发器实现审计日志记录.zip
- 计算机科学教育-数据结构课程设计目标与实践
- 磁环组装自动压合平衡测试设备工程图机械结构设计图纸和其它技术资料和技术方案非常好100%好用.zip
- 医疗器械质量管理体系内审员试卷考题,GBT42061,ISO13485
- 三相交错LLC谐振仿真闭环,Y型联接(图1主回路图),自均流(图2三相谐振电流波形),软开关(图3是原边mos的驱动和DS和电流波形),每相移相120度(图4驱动波形),图5输出电压电流波形 ,送对
- VSCode下C/C++开发环境配置指南
- MATLAB语音识别 matlab语音识别,可以识别数字0-9,有gui界面,注释齐全,有报告 (本程序测试版本为Matlab 2019b,低于此版本的请安装新版,以免无法运行) 链接为电子资料
- Java开发IDE-IntelliJ IDEA的下载与安装指南
- 线控转向系统Carsim和Simulink联合仿真模型,带Carsim数据库,C级车 【正向建模,利用三环PID控制算法控制无刷直流电机获得前轮转角】 主要根据Carsim自带的转向系统,查出小齿轮