JavaScript之闭包篇

157 阅读2分钟

在面试中,我们经常会被问及闭包相关的问题。这也是一个经常使用,但容易被我们忽略的知识点。下面,本篇文章将用最通俗易懂的语言,带领大家揭开闭包的神秘面纱。让大家清楚的知道什么是闭包以及闭包有什么好处、应用在哪里、使用闭包需要注意的地方有哪些......

1. 闭包的特点

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

例如:

// 第一个函数
function aaa(a){
    const b = 5;
    return function bbb(){
       alert(a)
       alert(b)
    }
 } 
 aaa(6)
// 第二个函数
function aaa(a){
    const b = 5;
}
aaa(6)

第二个函数执行完成后,参数和变量会立即回收,而第一个不会,因为第一个利用了js的闭包,避免了js中垃圾回收机制的触发。

2. js中的垃圾回收机制

function aaa(){
    conat a = 5
    function bbb(){
        alert(a)
    }
    return bbb;
}
const c = aaa()
c()

3. 闭包有什么好处

  1. 希望一个变量长期驻扎在内存当中
  2. 避免全局变量的污染
let a = 1
function aaa(){
    a++;
    alert(a)
}
aaa()
aaa()
alert(a)

上面的写法,虽然可以弹出a的值,但是因为a是全局变量,会造成全局污染的问题,我们可以利用闭包来改写一下。

我们先把变量放在函数里面:

function aaa(){
    let a = 1;
    a++;
    alert(a)
}
aaa()
aaa()

通过运行代码可以发现,弹窗的值一直是2,因为每一次调用方法,变量的值就被初始化了,下面我们把返回值改成一个函数。

function aaa(){
    let a = 1;
    return function(){
        a++;
        alert(a)
    }
}
const b = aaa()
b()
b()

运行代码,观察弹窗的值,发现已经实现了我们的要求。不过上述的写法,还是有些繁琐,我们可以再改写一下。

const aaa = (function(){
    let a = 1;
    return function(){
        a++;
        alert(a)
    }
})()
aaa()
aaa()

4. 闭包的用法

  1. 私有成员,模块化代码
  2. 在循环中直接找到元素的索引
const aaa = (function(){
    let a = 1;
    function bbb(){
        a++;
        alert(a)
    }
    function ccc(){
        a++;
        alert(a)
    }
    return {
        b:bbb,
        c:ccc
    }
})()

aaa.b()
aaa.c()

下面来看一个很经典的面试题

<body>
    <ul>
        <li>列表第一项</li>
        <li>列表第二项</li>
        <li>列表第三项</li>
    <ul>
    <script>
        const aLi = document.getElementsByTagName('li');
        for(var i=0;i<aLi.length;i++){
            aLi[i].onclick = function(){
                alert(i)
            }
        }
    </script>
</body>

当我们点击列表时,会发现,弹窗的值并不是我们想象当中的0、1、2而是3,原因也很简单,for循环给列表增加了点击事件,但是并没有调用,等到调用的时候,i的值是最后的3。

下面是利用闭包特性的改造写法:

const aLi = document.getElementsByTagName('li')
// 做法1
for(var i=0;i<aLi.length;i++){
    (function(i){
        aLi[i].onclick = function(){
            alert(i)
        }
    })(i)
}
// 做法2
for(var i=0;i<aLi.length;i++){
    aLi[i].onClick = (function(i){
       return function(){
           alert(i)
       }
    })(i)
}