JavaScript 闭包

2014年6月8日

本文写于 2014 年。当时常用 var,下文「没有块级作用域」针对的是这一语境;ES6 起 let / const 已引入块级作用域,但闭包概念仍然成立。

JavaScript 是一门函数式的编程语言,函数是一等公民,能作为传递参数,也可作为返回值,这样使闭包成为了可能。

作用域

说到闭包,不能不提作用域。在当时的 JavaScript 中(以 var 为主),没有块级作用域,只有函数作用域:作用域内能访问外部变量,但作用域外无法访问内部的变量。

123456789101112131415var i;for(i=0; i<3; i++){	var i = 3;	console.log(i);}var a=2;function test(){	var a = 3;	var b = 1;	console.log(a); //3}test();console.log(a); //2console.log(b); //error: b is not defined

第一个例子中 for 循环并不会形成作用域,所以只会循环一次,第二个例子中最后会报错,因为 b 的赋值是在函数作用域内的,所以外部是无法访问 b 的。

闭包

那如何在外部访问作用域内的变量?这里就要用到闭包,其实就是在函数的最后返回另外一个函数,返回的函数拥有父级函数内部变量的引用

123456789function demo(){	var a = 0;	return function(){		return a;	}}var e = demo();var x = e()//0

这里的 e 和 a 就形成了一个闭包,e 为 demo 的返回函数,而这个返回函数拥有 demo 中变量 a 的引用

闭包的坑

12345for(var i=0; i<3; i++){	setTimeout(function(){		console.log(i);	},100);}

上面的代码并不会愉快的输出 0, 1, 2,而是输出三个 3 ,闭包只是拥有函数中变量的引用,而不是值,所以上面的代码中匿名函数的 i 只是外部变量 i 的引用,所以当过了 100ms 后调用匿名函数,这时循环已完成,i 的值为 3, 所以输出三个 3,修正上面的错误,我们要用到自执行函数传值

1234567for(var i=0; i<3; i++){	(function(e){		setTimeout(function(){			console.log(e);		},100);	})(i)}

一般情况下,函数执行完成之后,再没有价值,会被内存回收。但闭包中,返回函数一直有函数中变量的引用,执行后并不会被回收,所以不要过多的使用闭包,以至于造成内存溢出。

变量提升

1234567var d = 0;function func(){	console.log(d);	var d = 1;}func();

猜猜这段代码会输出什么日志,答案是 undefined,因为 javascript 每次执行时,都会把变量提升到作用域的顶部,以上代码会解析成这样

123456789var d;d = 0;function func(){	var d;	console.log(d);	d = 1;}func();

评论

0 条讨论

匿名评论需填写昵称、邮箱,并通过人机验证。邮箱不会公开展示,提交后需审核通过才会显示。

还没有评论,来抢沙发吧。