本文深入解析JavaScript闭包的定义、产生条件及执行时机,通过代码示例展示其作为实参传递、计数器封装等常见应用场景。同时探讨闭包在自定义模块封装中的价值,并针对this指向错误及内存泄露问题提供具体的解决方案,帮助开发者深入理解并安全使用闭包。
前言
JavaScript中的闭包概念往往让初学者感到困惑。本文旨在从闭包的基本定义、典型示例、核心作用、实际应用场景以及常见陷阱等多个维度进行系统梳理,力求帮助读者建立对闭包的深入认知。文中如有疏漏或不当之处,欢迎指正交流。
一、什么是闭包?
闭包的本质是一个嵌套的内部(子)函数,它引用了父函数作用域中的数据,从而形成了闭包结构。
理解闭包需把握以下核心要点:
1. 必须存在函数的嵌套关系; 2. 闭包本身是一个函数,且特指那个嵌套的内部函数; 3. 该内部函数必须引用父函数作用域中的变量或数据。
若上述任一条件不满足,则无法形成闭包,下文将通过具体代码示例加以说明。
1.1 满足闭包条件的代码示例
<script>
function person(){
var name='marshal';
function student(){ //声明子函数
console.log(name);//引用父函数作用域的变量name
}
}
person();//函数执行,产生闭包
</script>

1.2 闭包的产生时机
<script>
function person(){
var name='marshal';//js执行此行时,产生闭包
function student(){ //声明子函数
console.log(name);//引用父函数作用域的变量name
}
student();//内部函数在外部函数调用
}
person();//函数执行,虽满足闭包条件,但未产生闭包
</script>

闭包产生的确切时机是:当嵌套子函数的代码块引用了父函数作用域的数据,且该子函数即将执行、创建执行上下文时,闭包随之产生。简而言之,当嵌套子函数在外部被调用执行的那一刻,闭包即被创建。
<script>
function person(){
var name='marshal';
function student(){
console.log(name); //该方法代码内为闭包代码
}
return student;
}
var p=person();//因创建子函数对像,此时产生第一次闭包,并将子函数student返回给p,由于p没有消失,子函数引用的变量name,一直在内存在存储,直到将p=null,进行回收
p();//执行子函数的闭包代码块,输出"marhsal"
p();//第二次执行子函数的闭包代码块,输出"marhsal"
person();//第二次创建子函数调对象,此时产生第二次闭包,但不执行子函数student代码块
</script>
二、常见闭包示例
2.1 将子函数作为实参传递
<script>
function setTimeoutTest(message,time){
setTimeout(function(){
alert(message);//嵌套子函数引用父函数变量message,产生闭包
},time);
}
setTimeoutTest('提示信息',1000);
</script>
2.2 计数器实现(通过函数返回值)
<script>
function count(){
var i=1;
function add(){
i++;
console.log(i);
}
return add;
}
var c=count();//生产闭包
c();//2
c();//3
c();//4
</script>
三、闭包的作用
3.1 闭包的主要作用
1)子函数引用父函数的变量或函数,从而延长其生命周期;
2)使得这些变量或函数在外部持续存在,允许外部访问函数内部的值。
<script>
function count(){
var i=1;
function add(){
i++;
console.log(i);
}
return add;
}
var c=count();
c();//2
c();//3 i的生命周期延长
</script>
四、闭包的应用
4.1 用于自定义封装JavaScript代码
外部js代码 out.js 实现自加与自减
(function count(){
var i=1;
function add(){
i++;
console.log(i);
}
function subtract(){
i--
console.log(i);
}
window.count={
add:add,
subtract:subtract
}
})();
引用 out.js代码
<script src=out.js></script>
<script>
count.add(); //2
count.subtract();//1
count.subtract();//0
</script>
五、闭包相关问题
5.1 闭包与this关键字的指向问题
<script>
var name="marshal"; //创建全局变量
var person={
name:"leo",
getName:function(){ //返回匿名函数
return function(){ //返回this.name
return this.name; //返回字符串
}
}
};
alert(person.getName()()); //输出marshal,内部函数不可能直接访问外部函数this
</script>
解决方法
<script>
var name="marshal";
var person={
name:"leo",
getName:function(){
var that=this;//把this保存到闭包可以访问的另一个变量中
return function(){
return that.name;
}
}
};
alert(person.getName()());//that 指向person,而不是window
</script>
5.2 内存泄露问题
在使用闭包时,由于引用的变量会一直驻留在内存中,需要注意及时解除对对象的引用。具体做法是将相关对象设置为null,从而确保其占用的内存在适当时候能够被垃圾回收机制回收。
至此,关于JavaScript闭包原理及其应用的详解已介绍完毕。如需了解更多相关JS闭包与应用内容,建议搜索本站历史文章或继续浏览后续相关教程。感谢大家的支持!