javascript的作用域分为两种:全局和局部。js作用域环境中访问变量的权利是由内向外的,内部的作用域可以获得当前作用域下的变量并且可以获得当前包含当前作用域的外层作用域下的变量。反之则不能,也就是说外层作用域下无法获取内层作用域下的变量,同样不同函数作用域中也是不能相互访问彼此的变量的。那么我们想在一个函数内部访问另外一个函数内部的变量怎么办?
闭包的本质就是在一个函数的内部创建另外一个函数。
闭包三个特性:
1、函数嵌套函数
2、函数内部可以引用函数外部的参数和变量
3、参数和变量不会被垃圾回收机制回收
闭包主要两种形式:
1、函数作为返回值
function a() { var name = 'nayn'; return function() { return name } } var b = a(); console.info(b())//nayn
a()中的返回值是一个匿名函数,这个函数在a()作用域内部,所以他可以获取a()作用域下变量name的值,将这个作为返回值赋给全局作用的变量b,实现了在全局变量下获取局部变量下的值。
function fn () { var num = 3 return function() { var n = 0; console.info(++n); console.info(++num); } } var fn1 = fn() fn1()// 1 4 fn1()// 1 5
一般情况下,在函数fn执行完后,就应该连同她里面的变量一同被销毁,但是在这个例子中,匿名函数作为fn的返回值被赋给了fn1,这个时候相当于fn1=function(){var n=0...},并且匿名函数内部引用者fn里面变量num,所以变量num无法被销毁,而变量n是每次调用时创新的,所以每次fn1执行完后她就把属于自己的变量连同自己一起销毁,于是乎最后就剩下孤零零的num,于是这里就产生了内存消耗的问题。
2、闭包作为参数传递
var num = 15; var fn1 = function(x) { if(x>num) { console.info(x) } } void function(fn2) { var num = 100; fn2(30) }(fn1)
函数fn1作为参数传入立即执行函数中,在执行到fn2(30)的时候,30作为参数传入fn1中,这时候if(x> num)中的num取的并不是立即执行函数中的num,而是取创建函数的作用域中的num这里函数创建的作用域是全局作用域下,所以num取得是全局作用域下的值15,即30>15打印30。
好处:
1、保护函数内的变量安全,实现封装,防止变量流入其他环境发生命名冲突
2、在内存中维持一个变量,可以做缓存(但是使用多了,消耗内存)
3、匿名执行函数可以减少内存消耗
坏处:
被引用的私有变量不能被销毁,增大了内存消耗,造成内存泄漏,解决的办法是可以在使用完变量后手动为它赋值为null;
由于闭包涉及跨域访问,所以会导致性能损失,我们可以通过把跨域作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行的速度。