函数知识点---闭包

本文深入探讨JavaScript中的作用域链与闭包概念,解释变量在不同作用域中的可见性,展示闭包如何保留对父级作用域的引用,以及在实际编程中的应用实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作用域链

JavaScript中存在大括号级的作用域,但他有函数作用域,在函数内定义的变量函数外是不可见的,如果该变量在if或者for语句中,那他在代码中是可见的;

 

var a=1;
function f(){
	var b=1;
	return a;
};

输出结果为

在这里a变量是全局作用域,而b变量的作用域在函数f()内,所以在f()内,a和b都是可见的,在f()外,a可见b不可见。

var global=1;
function outer(){
	var outer_local=2;
	function inner(){
		var inner_local=3;
		return inner_local+outer_local+global;
	};
	return inner();
};

 作用域链演示

闭包

每个函数都可以认为是一个闭包,因为每个函数都在其所在作用域中维护了某种私有联系,大多数时候,该作用域在函数体执行完之后就自行销毁了;

可以这样说,如果一个函数会在其父级函数返回之后留住父级作用域链接的话,相关闭包就会创建起来,但其实每个函数本身就是一个闭包,因为每个函数至少都有访问全局作用域的权限,而全局作用域是不会被破坏的

闭包#1

var a='global variable';
var F=function(){
	var b='local variable';
	var N=function(){
		var c='inner local';
		return b;
	};
	return N;
};

全局中不能直接访问b变量 

函数N有自己的私有空间,同时也可以访问F()的空间和全局空间,所以b对它来说是可见的,因为F()是可以在全局空间中被调用的(它是一个全局函数),所以我们可以将它的返回值赋给另一个全局变量,从而生成一个可以访问F()私有空间的新全局函数。

闭包#2

var inner;
var a='global variable';
var F=function(){
	var b='local variable';
	var N=function(){
		return b;
	};
	inner=N;
};

输出结果

闭包#3

function F(param){
	var N=function(){
		return param;
	};
	param++;
	return N;
};

输出

注意:当我们返回函数被调用时,param++已经执行过一次递增操作了,所以inner返回的是更新后的值。由此可以看出,函数所绑定的是作用域本身,而不是在函数定义时该作用域中的变量或变量当前的返回值

 循环中的闭包

function F(){
	var arr=[],i;
	for(i=0;i<3;i++){
		arr[i]=function(){
			return i
		};
	};
	return arr;
};

输出结果 

按通常估计,它们应该会依照循环顺序分别输出0、1和2,结果并不是

这并不是我们想要的结果,我们这里创建了三个闭包,它们都指向了共同的局部变量i,但是闭包并不会记录它们的值,它们所拥有的是相关域在创建时的一个连接(引用)。在这个例子中,变量i恰巧存在于定义这三个函数域中。对这三个函数中的任何一个而言,当要去获取某个变量时,它会从其所在作用域诸暨寻找距离最近的 i 值。由于循环结束时,i  的值为3,所以这三个函数指向了这一共同值。

那么如何纠正的呢,换一种闭包形式:

function F(){
	var arr=[],i;
	for(i=0;i<3;i++){
		arr[i]=(function(x){
			return function(){
				return x;
			};
		}(i));
	};
	return arr;
};

输出结果

在这里,我们不再直接创建一个返回 i 的函数了,而是将 i 传递给了另一个即时函数,在该函数中,i 就被赋值给了局部变量 x ,这样一来,每次迭代中的 x 都会拥有各自不同的值了         

闭包的应用示例

首先是创建 getter 和 setter。假设现在有一个变量,它所表示的是某一类特定的值,或某特定区间的值。我们不想将该变量暴露给外部。因为那样的话,其他部分的代码就有直接修改它的可能,所以我们需要将它保护在相关函数的内部,然后提供两个额外的函数,一个用于获取变量值,另一个用于给变量赋值。并在函数中引入某种验证措施,以便在赋值之前给予一定的保护。

我们需要将 getter 和 setter 这两种函数放在一个共同的函数中,并在该函数中定义secret变量,这使得两个函数能共享同一个作用域

var getValue,setValue;
(function(){
	var secret=0;
	
	getValue=function(){
		return secret;
	};
	
	setValue=function(v){
		if(typeof v==='number'){
			secret=v;
		};
	};
}());

输出结果:

用闭包制作next()迭代器

通常情况下我们都知道如何用循环来遍历一个简单的数组,但是有时候我们需要面对更为复杂的数据结构,通常会有着与数组截然不同的序列规则,这时候就需要将一些“谁是下一个“的复杂逻辑封装成易于使用的next()函数,然后我们只需要简单的调用next()就可以实现对于相关的遍历操作了。

如下例子中,一个接受数组输入的初始化函数,我们在其中定义了一个私有指针 i ,该指针会始终指向数组中的下一个元素。

 

function setup(x){
	var i=0;
	return function(){
		return x[i++];
	};
};

输出:

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值