JavaScript中闭包的概念、原理、作用及应用

一、闭包概念

闭包:有权访问另一个函数作用域中的变量的函数;一般情况就是在一个函数中包含另一个函数。
从官方定义我们知道闭包是一个函数,只不过这个函数可以访问到另一个函数的作用域。
为什么这个函数可以访问到另一个函数的作用域呢?
我们知道函数作用域是独立的、封闭的,外部的执行环境是访问不了的,但是闭包具有这个能力和权限。
那闭包是怎样的一个表现形式呢?
第一,闭包是一个函数,而且存在于另一个函数当中
第二,闭包可以访问到父级函数的变量,且该变量不会销毁

下面用代码举一个闭包的小例子:

function parentFun(){
	var name = '张三';
	function son(){
		console.log(name);
	}
	return son;
}
var p = parentFun();//p的值就是parentFun这个函数return后的结果,即son函数
p();//输出结果:张三 p()就相当于son()
p();//输出结果:张三 同上,而且变量name没有销毁,一直存在内存中,供函数son调用
p();//输出结果:张三

二、闭包原理

闭包的实现原理,其实是利用了作用域链的特性,我们都知道作用域链就是在当前执行环境下访问某个变量时,如果不存在就一直向外层寻找,最终寻找到最外层也就是全局作用域,这样就形成了一个链条。

例如:

var id = 1;
function selfAdd(){
    id++;
    console.log(id);// selfAdd函数内输出id,该作用域没有,则向外层寻找,结果找到了,输出[2];
}
selfAdd();//输出结果:2
//看到这里,大家都会说这不就是最简单的函数和变量形式吗?闭包在哪里?别急,我们接着往下看:
//如果我们再次调用,结果会一直增加,也就变量id的值一直递增。
selfAdd();//输出结果:3
selfAdd();//输出结果:4
selfAdd();//输出结果:5

如果程序还有其他函数,也需要用到id的值,则会受到影响,而且全局变量还容易被人修改,比较不安全,这就是全局变量容易污染的原因,所以我们必须解决变量污染问题,那就是把变量封装到函数内,让它成为局部变量。

function parentFun(){
	var id = 1;
	function son(){
		id++;
		console.log(id);
	}
	return son;
}
parentFun()();//输出结果:2
parentFun()();//输出结果:2
//解释一下这里为什么要加两对括号
//因为parentFun()函数返回的是son无括号,不加两对括号输出结果为空
//也可以在parentFun()函数中写成return son();则无需加两对括号调用parentFun()函数

这里又出现问题了,每次调用函数 parentFun,进入该作用域,变量 id 就会重新赋值为1,所以 son 的返回值一直是2;所以需要做一下调整:

var p = parentFun();//p的值就是parentFun这个函数return后的结果,即son函数
p();//输出结果:2 p()就相当于son()
p();//输出结果:3 这样每次调用不在经过id的初始值,这样就可以一直增加了
p();//输出结果:4
//而且变量id在函数内部,不易被修改和外泄,相对来说比较安全

三、闭包作用

作用1:隐藏变量,避免全局污染
作用2:可以读取函数内部的变量
同时闭包使用不当,优点就变成了缺点:
缺点1:导致变量不会被垃圾回收机制回收,造成内存消耗
缺点2:不恰当的使用闭包可能会造成内存泄漏的问题
这里简单说一下,为什么使用闭包时变量不会被垃圾回收机制收销毁呢,这里需要了解一下JS垃圾回收机制;
JS规定在一个函数作用域内,程序执行完以后变量就会被销毁,这样可节省内存;
使用闭包时,按照作用域链的特点,闭包(函数)外面的变量不会被销毁,因为函数会一直被调用,所以一直存在,如果闭包使用过多会造成内存销毁。

四、闭包应用

需求:实现变量a 自增

1、通过全局变量,可以实现,但会污染其他程序

var a = 10;
function numberAdd(){
	a++;
	console.log(a);
}
numberAdd();//输出结果:11
numberAdd();//输出结果:12
numberAdd();//输出结果:13

2、定义一个局部变量,不污染全局,但是实现不了递增

function numberAdd2(){
	var a = 10;
	a++;
	console.log(a);
}
numberAdd2();//输出结果:11
numberAdd2();//输出结果:11
numberAdd2();//输出结果:11

3、通过闭包,可以是函数内部局部变量递增,不会影响全部变量,完美!!

function numberAdd3(){
	var a = 10;
	return function(){
		a++;
		console.log(a);
	}
}
var num = numberAdd3();
num();//输出结果:11
num();//输出结果:12
num();//输出结果:13

最后您可以将三种方法放在一起,自己再写一写干扰变量 a 全局值的代码,看看效果
完整代码(不包含干扰代码):

var a = 10;

//1.通过全局变量,可以实现,但会污染其他程序
function numberAdd(){
	a++;
	console.log(a);
}

//2.定义一个局部变量,不污染全局,但是实现不了递增
function numberAdd2(){
	var a = 10;
	a++;
	console.log(a);
}

//3.通过闭包,可以是函数内部局部变量递增,不会影响全部变量,完美!!
function numberAdd3(){
	var a = 10;
	return function(){
		a++;
		console.log(a);
	}
}

numberAdd();//输出结果:11
numberAdd();//输出结果:12
numberAdd();//输出结果:13

console.log('------以上为numberAdd()函数的输出结果-------');

numberAdd2();//输出结果:11
numberAdd2();//输出结果:11
numberAdd2();//输出结果:11

console.log('------以上为numberAdd2()函数的输出结果-------');

var num = numberAdd3();
num();//输出结果:11
num();//输出结果:12
num();//输出结果:13

console.log('------以上为numberAdd3()函数的输出结果-------');

console.log(a);//输出结果:13

风影OvO

风影OvO, 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA 4.0协议进行授权 | 转载请注明原文链接

留下你的评论

*评论支持代码高亮<pre class="prettyprint linenums">代码</pre>

相关推荐