JavaScript闭包

闭包的简介

简单讲,闭包就是指有权访问另一个函数作用域中的变量的函数。

MDN 上面这么说闭包是一种特殊的对象。它由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成。

创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量


闭包的特性:

  1. 函数嵌套函数
  2. 函数内部可以引用外部的参数和变量
  3. 参数和变量不会被垃圾回收机制回收

闭包的优缺点

优点:

  1. 希望一个变量长期驻扎在内存中
  2. 避免全局变量的污染
  3. 私有成员的存在

缺点:

  1. 常驻内存,会增大内存使用量,使用不当很容易造成内存泄露,增大内存消耗。
function aaa() {  
    var a = 1;  
    return function(){
        alert(a++)
    };  
}         
var fun = aaa();  
fun();// 1 执行后 a++,,然后a还在~   a会长期驻扎在内存中
fun();// 2   
fun = null;//a被回收!! 

javascript的垃圾回收原理

(1)在javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收; 
(2)如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。


避免全局变量的污染

// 全局变量a 可以实现累加
var a = 1;
function aaa() {
    a++;
    alert(a);
}
aaa(); // 2 
aaa(); // 3

// 局部变量b 不能实现累加
function bbb() {
    var b = 1;
    b++;
    alert(b);
}
bbb(); //2
bbb(); //2


// 局部变量c 使用闭包 可以实现累加
// 减少了全局变量
var ccc = (function () {
    var c = 1;
    return function () { // 闭包
        c++;
        alert(c);
    }
})
ccc(); // 2
ccc(); // 3


循环中的点击事件

<ul>
    <li>111</li>
    <li>222</li>
    <li>333</li>
    <li>444</li>
</ul>

<script>
    var aLi = document.getElementsByTagName('li');
    
    // 使用var声名 i 是全局变量,循环的i都是同一个
    for (var i = 0; i < aLi.length; i++) {
        aLi[i].onclick = function () { // 当点击时for循环已经结束                   
            alert('当前点击的是第' + i + '个'); // 一直都是4
        };
    }

    // 使用let声名 i 改写上面代码,循环的i只在本轮循环有效
    for (let i = 0; i < aLi.length; i++) {
        aLi[i].onclick = function () { // 每一次循环的i都是一个新的变量                
            alert('当前点击的是第' + i + '个'); 
        };
    }

    // 使用闭包改写上面代码
    for (var i = 0; i < aLi.length; i++) {
        (function (i) {
            aLi[i].onclick = function () {
                alert('当前点击的是第' + i + '个'); // 0 1 2 3 
            };
        })(i)
    }
</script>

循环中的定时任务

for (var i = 1; i < 4; i++) {
    setTimeout(function () {
        console.log(i);
    }, 1000); // 一直都是3
}

// 这是因为setTimeout中的匿名函数执行的时候,for循环都已经结束了
// 究其原因:i是声明在全局作用中的,定时器中的匿名函数也是执行在全局作用域中,那当然是每次都输出3了

// 我们可以让i在每次迭代的时候,都产生一个私有的作用域,在这个私有的作用域中保存当前i的值
for (var i = 1; i < 4; i++) {
    (function (j) {
        setTimeout(function () {
            console.log(j);
        }, 1000); // 1 2 3
    })(i)
}

闭包的应用

1. 闭包的应用比较典型是定义模块,我们将操作函数暴露给外部,而细节隐藏在模块内部:

function module() {
    var arr = [];

    function add(val) {
        arr.push(val);
    }

    function get(index) {
        if (index < arr.length) {
            return arr[index]
        } else {
            return null;
        }
    }
    return {
        add: add,
        get: get
    }
}
var mod1 = module();
mod1.add(1);
mod1.add(2);
mod1.add('xxx');
console.log(mod1.get(2));

2. 防抖与节流

传送门


闭包引起的内存泄漏

将不使用的局部变量全部删除,可以使变量赋值为null

//这段代码会导致内存泄露
window.onload = function () {
    var el = document.getElementById("id");
    el.onclick = function () {
        alert(el.id);
    }
}

//解决方法为
window.onload = function () {
    var el = document.getElementById("id");
    var id = el.id; //解除循环引用
    el.onclick = function () {
        alert(id);
    }
    el = null; // 将闭包引用的外部函数中活动对象清除
}

posted @ 2022-07-20 18:17  猫老板的豆  阅读(135)  评论(0编辑  收藏  举报