JavaScript闭包原理与实战案例详解

作者:异步漫步时间:2026-10-28 15:58:52浏览:247

本文深入解析JavaScript闭包的核心原理,通过对比PHP语言阐明函数作用域与链式作用域的差异。文章提供了多个经典闭包案例,包括计数器实现、参数传递以及解决循环中异步回调变量共享问题的方案,帮助开发者掌握闭包在数据封装与状态保持中的实际应用。

JavaScript闭包基础与作用域

JavaScript闭包 理解闭包前需掌握的基础知识 1. 函数作用域 (1). JavaScript语言的独特之处在于函数内部可以直接访问全局变量

代码如下:

<script type="text/javascript">
var n=100;
function parent(){
alert(n);
}
parent();//输出 100
</script>

如果在PHP语言中

代码如下:

<?php
$n=100;
function parent(){
echo $n;
}
parent();//会报错 n未定义
?>

(2).在函数外部无法直接读取函数内部的局部变量

代码如下:

<script type="text/javascript">
function parent(){
var m=50;
}
parent();
alert(m);//报错 m未定义
</script>

注意在函数内部声明变量时务必使用var关键字,否则该变量将变为全局变量

代码如下:

function parent(){
m=50;
}
parent();
alert(m);//输出 50

//在PHP中同样如此,

代码如下:

<?php
function parent(){
global $m;//全局变量,定义与赋值需分开处理
$m=50;
}
parent();
echo $m;//输出 50
?>
//若未使用global关键字,同样会报未定义错误

有时需要获取函数内部的局部变量,可以通过利用JavaScript变量作用域的特性来变通实现。例如在函数内部定义子函数,对于子函数而言,父函数相当于其全局作用域,子函数可以访问父函数中的变量(尽管对整体JavaScript代码而言这些变量是局部的)。

代码如下:

<script type="text/javascript">
function parent(){
var m=50;
function son(){
alert(m);
   }
return son;
}
var s=parent();//将结果保存在全局变量中
s();//输出 50
</script>

Parent内部的所有局部变量对其子函数可见,但其子函数内的局部变量对父函数不可见,这就是JavaScript特有的链式作用域结构。子对象会一级一级向上查找所有父对象的变量,父对象的所有变量对子对象都是可见的,反之不成立!上述son函数即为闭包。 部分同学可能会尝试以下写法

代码如下:

function parent(){
var m=50;
function son(){
alert(m);
   }
}
parent();
son()//会报错 函数son未定义

注意在JavaScript中,在函数内声明的函数都是局部的,函数运行结束后即被释放。 这一点与PHP存在区别

代码如下:

<?php
function parent(){
function son(){
$m=50;
echo $m;
  }
}
parent();
son();//输出 50 不会报错
?>

闭包的定义与核心作用

闭包

函数内部定义函数,它是连接函数内部与外部的桥梁。 闭包主要有两个作用: 一是前文提到的读取函数内部的变量, 二是让这些变量的值保留在内存中,实现数据共享。 以下是几个闭包的示例

闭包基础实例:计数器与参数传递

代码如下:

<script type="text/javascript">
var cnt=(function(){
var i=0;
return function(){
alert(i);
i++;
    }
})();
cnt();//输出 0
cnt();//输出 1
cnt();//输出 2
cnt();//输出 3
</script>

将匿名函数的执行结果(即内部子函数的声明)赋给全局变量cnt,变量i便保存在内存中。 执行cnt()时直接从内存取值,i只有cnt()函数才能调用,直接alert(i)是无法实现的。 还可以向闭包内传递参数

代码如下:

var cnt=(function(num){
return function(){
alert(num);
num++;
  }
})(5);
cnt();//输出 5
cnt();//输出 6
cnt();//输出 7
//当然也可以在调用时传递参数
var cnt=(function(){
var i=0;
return function(num){
num+=i;
alert(num);
i++;
  }
})();
cnt(1);//输出 1
cnt(2);//输出 3
cnt(3);//输出 5

闭包实战:解决循环变量共享问题

为了更深入理解闭包,我们分析以下代码。 假设我想返回一个数组,数组中包含5个函数,第一个函数弹出0,第二个弹出1... 如果代码这样编写

代码如下:

function box(){
var arr=[];
for(i=0;i<5;i++){
arr[i]=function(){return i;}
    }
return arr;
}
var a=box();
alert(a);//包含五个函数体的数组
alert(a[0]());
alert(a[1]());

弹出的函数体 function(){return i;} } 最后这个i是4,之后++成为5 For循环停止 发现均弹出5,明显不符合我们的要求

解决方案1 使用立即执行函数包裹内部逻辑

代码如下:

function box(){
var arr=[];
for(i=0;i<5;i++){
arr[i]=(function(num){return i;})(i);
    }
return arr;
}
var a=box();
for(var i=0;i<a.length;i++){
alert(a[i]());
}

但是我们发现返回的数组里的元素是函数执行的结果,但我们想要的是函数本身,因此需要升级我们的代码

解决方案2 利用闭包实现

代码如下:

function box(){
var arr=[];
for(var i=0;i<5;i++){
arr[i]=(function(num){
 return function(){return num;}
 })(i);
}
return arr; 
}
var arr=box();
for(var i=0;i<5;i++){
alert(arr[i]());//输出 0,1,2,3,4
}

关键代码

代码如下:

arr[i]=(function(num){
return function(){return num;}
})(i);
当 i=0 时 
arr[0]=(function(num){return function(){return num;}})(0);
当 i=1 时
arr[1]=(function(num){return function(){return num;}})(1);

以上就是闭包的好处!非常简单实用吧。

作者最新文章