一、闭包的特点

    1.在函数内部嵌套函数;

    2.内部函数使用外部函数的参数和变量

    3.重点:函数内部的变量(函数参数)不会被浏览器的垃圾回收机制回收,闭包可以使得它的诞生环境一直存在。

    闭包使得函数内部环境一直存在,除非非手动销毁,将值设为null;

    简单的理解:

    闭包函数就是一个声明在函数内部的函数;

    闭包就是内部函数总是可以访问外部函数的参数和变量,并且在外部函数被返回后仍然可以访问;

    综合理解:闭包就是可以创建一个独立的环境(在内存中),每个闭包里面的环境都是独立的,互不干扰。闭包会发生内存泄漏,每次外部函数执行的时 候,外部函数的引用地址不同(将外部函数赋值给一个外部变量或者叫活动对象),都会重新创建一个新的地址。只要当前活动对象(外部变量)中有被内部子集(内部指函数内部)引用的数据,那么这个时候,这个数据不删除,保留一根指针给内部活动对象。

    例子:这是一段闭包改造后的自执行函数。 1.我们可以看见因为外部类数组 list 的子项被 for 循环内的函数内部子集调用了,每一次迭代都给 list 对应的子集在内存中开辟了一块独立区域存放参数 i 。 2.所以在 for 循环的迭代过程中,每一次迭代的结果 i 都被一个 list 子项在内存中保存下来。 3.在我们没有进行点击事件之前,代码最下方的打印会先打印一个4,因为点击事件相当于是一个异步函数,所以先把 for 循环的最终迭代结果打印出来。 4.在我们点击每个 list 子项时,会将每个 list 子项在内存中所保存的参数打印出来。
    1. var list = document.querySelectorAll('li'); //4个li
    2. for (var i = 0; i < list.length; i++) { //i:0,1,2,3
    3. (function (i) { //i是参数,相当于函数内部的变量,形成闭包,i一直存在。i:0,1,2,3
    4. list[i].onclick = function () {
    5. console.log(i); // 0,1,2,3
    6. }
    7. })(i); //i:0,1,2,3
    8. }
    9. console.log(i); //4,循环的最后一次值。

    循环和定时器的问题

    1. for (var i = 1; i <= 5; i++) {
    2. setTimeout(function() {
    3. console.log('hehe'); //定时器会被执行5次
    4. }, 1000);
    5. }
    6. for (var i = 1; i <= 5; i++) {
    7. setTimeout(function() {
    8. console.log(i); //定时器会被执行5次 异步:等到循环执行完成。 这里的值是6,因为定时器异步执行,而循环的执行顺序在定时器前,所以优先循环完5次定时器后,等定时器读取 i 的值的时候 i 就已经是6了
    9. }, 1000);
    10. }

    改为闭包实现

    1. for (var i = 1; i <= 5; i++) { // i:1,2,3,4,5
    2. ! function(i) { //i:i是参数,相当于每一轮循环的 i 的值保存进这个自循环函数中了,循环了五次就相当于创建了五个闭包,保存了五个 i 的值
    3. setTimeout(function() {
    4. console.log(i); //1,2,3,4,5
    5. }, 1000);
    6. }(i); //1,2,3,4,5
    7. }

    闭包的优缺点—面试题

    1.闭包的好处:

    希望一个变量长期存储在内存中(延长变量作用域) - 循环添加事件,里面的循环变量(循环里面使用定时器)

    避免全局变量的污染,局部变量(函数内部的变量)的存在。

    2.闭包的缺点:

    2.1.闭包使得内部的变量一直存在,变量常驻内存,增加内存使用量—手动销毁,变量的值设为null

    2.2.使用不当会造成内存泄漏

    内存泄露是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束。

    当变量标记为离开环境或者变量引用计数为0,而垃圾回收机制无法回收时,就会产生内存泄漏。

    经典面试题

    1. var name = "The Window"; // var声明的变量也是window下面的属性。
    2. var object = {
    3. name:"My Object",
    4. getNameFunc:function() { //this->object
    5. return function() {
    6. return this.name; //this->window,不是object直接调用,里面的方法返回的函数表达式直接使用。
    7. };
    8. }
    9. };
    10. alert(object.getNameFunc()); //返回的是getNameFunc内部的函数体
    11. alert(object.getNameFunc()()); //The Window
    12. var name = "The Window";
    13. var object = {
    14. name:"My Object",
    15. getNameFunc:function() { //this->object
    16. var that = this; //将指向object的this赋值给that,that指向object的this
    17. return function() {
    18. return that.name;
    19. };
    20. }
    21. };
    22. alert(object.getNameFunc()()); //My Object