JavaScript 学习笔记 之 作用域 (五) - 闭包

5.1 什么是闭包

当函数可以记住并访问定义时所在的(上下文)词法作用域时,就产生了闭包,即使函数是在定义时的词法作用域之外执行

//全局作用域
function foo() {//作用域1
	var a = 2;

	function bar() {
		console.log(a);
	}
	return bar;
}
var fun = foo();//记住了函数定义时候的作用域1、全局作用域
fun(); //2————这就是闭包

闭包发生在定义时。(只有调用了外部函数生成了内部作用域才能生成闭包,这点很重要)

当运行了var fun=foo();时,发生了闭包。

fun记住了此时作用域1、全局作用域

记住全局作用域其实意义不大,因为全局作用域只有一个,但每运行一次foo(),都会生成一个新的作用域1。

因为当运行完一次foo()时,JavaScript引擎都会销毁foo()的整个内部作用域,再次运行foo()时,又会生成一个新的内部作用域。

而发生闭包后,内部作用域并不会消失,因为fun,或者说bar()依然在使用在该内部作用域。

//全局作用域
function a(){//作用域1
	function b(){
	}
	return b;
}

var fun1=a();
var fun2=a();
console.log(fun1===fun2);//false

第一次调用a()时生成了一个作用域,第二次调用a()时又生成了一个全新的作用域,即使没有对内部作用域进行任何修改,fun1和fun2闭包记住的作用域也完全是两个作用域。这就是闭包的效果。

//全局作用域
function a(){
	return a;
}

var fun1=a();
var fun2=a();
console.log(fun1===fun2);//true

而在这个例子中,fun1和fun2所记住的都是全局作用域,因此fun1===fun2。

5.2 循环与闭包

为了更好的说明闭包,循环是个最常见的例子

(注:延迟函数的回调会做循环结束时才执行,即使执行的是setTimeout(...,0) ,但是定义发生在循坏过程中,因此定义时记下的作用域和执行时的作用域完全不同。)

这个函数在定义的词法作用域以外的地方被引用。闭包使得函数可以继续访问定义时的词法作用域。

//全局作用域
for(let i = 1; i <= 5; i++) {//作用域2
	(function() {//作用域1
		setTimeout(function timer() {
			console.log(i);
		}, 0);
	})();
}

由于使用的是let进行定义,因此i作为for循环的块作用域中的变量

for循环每个迭代中,定义的timer函数都记录下了有着不同的i值的作用域2

因此在定义的词法作用域以外的地方被引用时,输出的是不同的作用域2中的i值(1、2、3、4、5)。

//全局作用域
for(var i = 1; i <= 5; i++) {
	(function() {//作用域1
		setTimeout(function timer() {
			console.log(i);
		}, 0);
	})();
}

而如果使用var 进行定义,事实上i会被绑定到全局作用域

for循环的迭代中,定义的timer函数记录的是相同全局作用域中的i

因此在定义的词法作用域以外的地方被调用时,输出的是相同的全局作用域中的i(此时i循环后值为6),因此输出结果是5个6。

猜你喜欢

转载自blog.csdn.net/Aproducer/article/details/82227662