本文深入解析JavaScript闭包的形成原理,涵盖变量作用域提升、生命周期管理及词法环境引用机制。通过按钮点击案例演示闭包解决循环变量共享问题的方法,并介绍利用闭包实现私有变量保护及函数节流等实际应用场景。
1. 理解闭包
闭包的一个典型应用案例:利用 for 循环为前述五个按钮绑定点击事件。
<button type="button" class='button'>按钮</button>
<button type="button" class='button'>按钮</button>
<button type="button" class='button'>按钮</button>
<button type="button" class='button'>按钮</button>
<button type="button" class='button'>按钮</button>
var buttons = document.getElementsByClassName('button');
for (var i = 0; i < 5; i++) {
buttons[i].onclick = function() {
console.log(i+1);
}
}若直接点击这五个按钮,控制台均输出数字 5,此现象源于变量 i 的作用域机制导致代码未按预期执行。
通过引入闭包机制对原有代码进行优化。
var buttons = document.getElementsByClassName('button');
for (var i = 0; i < 5; i++) {
buttons[i].onclick = (function(i){
return function(){
console.log(i+1);
}
}(i))
}再次点击这五个按钮,控制台将依次显示数字 1、2、3、4、5。
2. 变量作用域与生命周期解析
2.1 变量作用域定义
- 作用域指的是变量在代码中可被访问的有效范围。
- 采用 var 关键字声明的变量存在提升现象至全局,而函数内部声明的变量仅限在函数体内访问。
- 采用 let 和 const 关键字声明的变量不具备提升特性。
在前述代码示例中,for 循环内的变量 i 由 var 声明,从而提升至全局作用域。由于在按钮点击事件触发前 for 循环已执行完毕,最终打印出的均为全局变量 i 的值,即数字 5。
在利用闭包进行代码优化时,通过立即执行函数(IIFE)将每次循环的变量 i 封闭在函数内部,从而确保最终输出结果为正确值。
2.2 变量生命周期
全局作用域内的变量生命周期为永久存在,而函数内部的变量在函数执行结束后即被回收并销毁。
当形成闭包时,由于函数的返回值(即另一个函数)依然引用着原函数的局部变量,这导致即使原函数执行完毕,其内部变量依然存在于内存中。闭包正是基于这一特性发挥作用。
3. 闭包定义及其功能
3.1 闭包定义
基于前述分析可知,闭包核心涉及变量的生命周期与作用域概念。构建闭包需同时满足以下三个必要条件:
- 函数的返回值必须是一个函数。
- 被返回的函数内部引用了外层函数的局部变量。
- 被返回的函数在外部作用域中被调用。
3.2 闭包的实际应用场景
3.2.1 实现私有变量存储
JavaScript 语言原生不提供 private 关键字,但可以通过闭包机制将变量封装在函数内部,以此达到保护变量不被外部直接访问的目的。
var getUserInfo = (function() {
var userInfo = {
name: 'ian',
age: 21
};
return function() {
return userInfo;
}
}());
console.log(getUserInfo()); //{name:'ian',age:21}
console.log(userInfo); //Uncaught ReferenceError: userInfo is not defined3.2.2 利用闭包实现节流函数
function throttle(fn,delay){
var timer=null;
return function(){
if(!timer){
timer=setTimeout(()=>{
fn();
timer =null;
},delay)
}
}
}
window.onresize= throttle(function(){
console.log('resize')
},500)
至此,关于 JavaScript 闭包原理与应用的介绍已告一段落。如需了解更多 JS 闭包相关内容,请查阅本站历史文章或继续浏览后续相关文章,感谢大家对本站的支持!