64、JavaScript 闭包

凡是满足以下三个特点,都属于闭包

1、 函数嵌套函数;

2、 内部函数使用外部函数的形参和变量;

3、 被引用的形参和变量就不会被垃圾回收机制所回收;

垃圾回收机制:

调用函数时,系统会分配一定的内存空间给这个函数使用(空间大小由这个函数里声明的变量和形参决定)。调用完毕之后,这个内存空间要释放,还给系统。

var a=0
function show(){


    console.log(a);
    a++;
}
show()
show()
//a的空间没被释放
alert(a)//不报错,正常显示

 

function show(){
   

        var a=0
        console.log(a);
        a++;
    }
show()
show()
//a的空间被释放
alert(a)//报错 not defined

一般情况下,函数外部是无法访问函数内定义的形参和变量的。
 
但是当我们将内部函数调用外部函数的变量和形参时,再将内部函数作为外部函数返回值,此时执行返回值函数时,是可以访问到函数内的变量和形参的。证明,在外部函数调用完毕之后,其定义的形参和变量并没有被立即销毁掉,而是保存在内部函数里了。只要内部函数没有被销毁,内部函数就能够不断地引用外部函数的变量和形参。
 
闭包的作用

1、 希望一个变量常驻在内存中2.避免全局变量污染,避免声明全局变量3.可以声明私有成员;

全局变量污染:当我们在一个页面内声明了很多变量和函数在全局作用域下,当我们将多个代码合成一个代码时,有可能就出现,全局作用下的变量和函数出现重名的情况,导致整个程序崩溃。
 
上图中,全局变量被不断的更改,但是如果其他人想要引用整个a的话,就是一个被更改了的a。

引起为了避免这种情况的发生,我们尽量去设置局部变量
 
如果要实现函数内的变量一直驻留在内存中,此时就需要闭包了
 
立即执行函数

原理:  
但是因为()的优先级是最高的,这样就会出现,括号内为空,就会报错,因此真正执行时,需要将前面的函数也加上()
 
更改我们第一个例子

let res=(function (){
   

    var a=10
    function show2(){


        a++;
        console.log(a);
    }
    return show2
})()

res()
res()

声明私有变量
 
案例:

保存变量:
 
将外部变量以形参的形式传入,并立即执行
 
优化:  
闭包注意事项

内存泄漏:被闭包占用的内存,已经被永久占用了,其他人无法再使用了,也就是所谓的钉子户
 
上图中,window.onload和obtn.onclick就构成了一个闭包。这样obtn就会一直驻留在内存当中。就造成内存泄漏。并且对象占用的内存比较大。

解决内存泄漏:主要出现在IE浏览器

解决方式一:减少内存泄漏,在外部函数使用变量存储需要用到值
 
解决方式二:利用IE浏览器独有的window.onunload 当页面解构的时候触发(刷新页面,关闭当前页面),在刷新的时候就释放内存