js闭包

207 阅读2分钟

闭包理解

当内部函数被保存到外部时,将会生成闭包。内部的函数在外部执行时可以调用原来在的那个函数环境里的变量。闭包会导致原有作用域链不释放,造成内存泄漏。

如以下这个例子:

function a(){
		var temp = 100;
		function b() {
			console.log(temp);
		}

		return b;
	}
	var demo = a();
	demo();
a defined a.[[scope]] --> 0:GO
a doing   a.[[scope]] --> 0:aAO
						  1:GO
b defined b.[[scope]] --> 0:aAO
						  1:GO
b doing   b.[[scope]] --> 0:bAO
						  1:aAO
						  2:GO

a函数执行结束后,执行上下文被销毁。这时函数b被保存到外部,虽然a的执行上下文被销毁,但函数b中保存了a的AO,所以执行函数b时,在a中定义的变量是可以使用的。所以上面程序中,执行函数a最后返回函数b,函数b被保存在demo中,调用函数demo(),是可以输出在a中定义的temp的。

【[[scope]]】: 每个JavaScript函数都是一个对象,对象中有些属性我们可以访问,但有些不可以,这个属性仅供JavaScript引擎存取,[[scope]]就是其中一个。[[scope]]指的就是我们所说的作用域,其中存储了执行期上下文的集合

【作用域链】: [[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,把这种链式链接叫做作用域链。

【执行期上下文】: 当函数执行时,会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数执行的环境,函数每次执行时对应的执行期上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行期上下文,当函数执行完毕,它所产生的执行上下文被销毁。

【查找变量】: 从作用域链的顶端依次向下查找。

闭包的一些作用

  • 实现公有变量

不依赖于外部变量,并且能反复执行的函数累加器

function add() {

		var num = 0;
		function a() {
			console.log(++num);
		}
		return a;
	}
	var myAdd = add();
	myAdd();

执行一次函数就加一,第一次调用函数输出1,第二次输出2,第三次输出3。

  • 可以做缓存(存储结构)

  • 实现封装,属性私有化

  • 模块化开发,防止污染全局变量

使用闭包后容易出现的错误

判断一下输出的结果

function test() {
		var arr = [];
		for(var i = 0; i < 10; i ++) {
			arr[i] = function () {
				document.write(i + " ");
							  //此时函数并不执行,这只是一条赋值语句,
                                等到函数执行时才会去判断i的值是什么再打印出来
			}
		}
		return arr;
	}

	var myArr = test();
	for(var j = 0; j < 10; j++){
		myArr[j]();
	}

最终会打印出10个10

解决

想要把0~9打印出来,可以利用立即执行函数

function test() {
		var arr = [];
		for(var i = 0; i < 10; i ++) {
        
        //立即执行函数
			(function(j){
				arr[j] = function() {
					document.write(j + " ");
				}
			}(i));
		}
		return arr;
	}

	var myArr = test();
	for(var j = 0; j < 10; j++){
		myArr[j]();
	}