[nodejs 内功心法][作用域与闭包系列四] 闭包

本文深入解析JavaScript中闭包的概念,展示其如何让函数记住并访问所在词法作用域,即使在外部执行也能访问内部变量。通过实例说明闭包在循环中的应用及解决方法。

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

闭包

闭包是js一个非常重要但是理解起来又有一定难度的概念,理解闭包能让你的js功力得到一个质变。

闭包的概念

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。

JavaScript中的函数会形成闭包。 闭包是由函数以及创建该函数的词法环境组合而成。这个环境包含了这个闭包创建时所能访问的所有局部变量。

function foo() {
	var a = "哈喽"

	function bar() {
		console.log(a) 
	}
	
	return bar
}

var baz = foo()
baz() // 哈喽
// 这就是闭包的效果!!!!!!

函数 bar() 的词法作用域能够访问 foo() 的内部作用域。这个例子bar()在自己定义的词法作用域以外的地方被正常执行。这就是闭包的威力。
这个函数在定义时的词法作用域以外的地方被调用。闭包使得函数可以继续访问定义时的词法作用域。

在 foo() 执行后,通常会期待 foo() 的整个内部作用域都被销毁,因为我们知道引擎有垃圾回收器用来释放不再使用的内存空间。由于看上去 foo() 的内容不会再被使用,所以很自然地会考虑对其进行回收。

而闭包的“神奇”之处正是可以阻止这件事情的发生。事实上内部作用域依然存在,因此没有被回收。是因为 bar() 在使用这个内部作用域!

拜 bar() 所声明的位置所赐,它拥有涵盖 foo() 内部作用域的闭包,使得该作用域能够一直存活,以供 bar() 在之后任何时间进行引用。
bar() 依然持有对该作用域的引用,而这个引用就叫作闭包

function foo() {
	var a = 2;

	function baz() {
		console.log(a);
  	}
  	bar(baz)
}

function bar(fn) {
	fn(); // 2  这就是闭包的威力
}

无论通过何种手段将内部函数传递到所在的词法作用域以外,它都会持有对原始定义作用域的引用,无论在何处执行这个函数都会使用闭包。

循环中的闭包

for (var i=1; i<=5; i++) {
	setTimeout(function timer() {
		console.log(i);
	}, i*1000)
}
// 6
// 6
// 6
// 6
// 6

全部会输出6, 因为所有函数共享一个 i 的引用

for (var i=1; i<=5; i++) {
	(function(j) {
		setTimeout(function timer() {
			console.log(j);
		}, i*1000)
	})(i)
}

// 1
// 2
// 3
// 4
// 5

我们可以用es6的let使用块作用域来解决这个问题

for (let i=1; i<=5; i++) {
	setTimeout(function timer() {
		console.log(i);
	}, i*1000)
}

// 1
// 2
// 3
// 4
// 5

for 循环头部的 let 声明还会有一 个特殊的行为。这个行为指出变量在循环过程中不止被声明一次,每次迭代都会声明。随 后的每个迭代都会使用上一个迭代结束时的值来初始化这个变量。

小结

当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时 就产生了闭包。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值