JS闭包

89 阅读3分钟

JS闭包

闭包是什么?为什么需要闭包

闭包就是能够读取其他函数内部变量的函数,在本质上,闭包是将函数内部和函数外部连接起来的桥梁

对百度得来的这句话进行逐字分析

1、能够获取其它函数内部变量?

我们都知道,js中,局部变量在全局是无法直接访问到的,如下

function f1(){
    var name = 'f1'
}
console.log(name);

相信大家都知道这里name并不是f1

2、闭包能够读取其它函数内部变量

如何读取?

3、闭包是个函数?

头大,分析到这里还是不明白,但我们似乎还是难以理解什么是闭包。

那我们还可以再掌握一些前提

变量的生命周期

  • 全局变量:页面关闭,结束生命周期
  • 局部变量,在函数执行完毕或者销毁,结束生命周期

为什么需要闭包?

闭包可以调用函数内部的变量,从而避免大量外部声明导致的变量污染

闭包的原理

当一个函数执行完毕后,正常情况下这个函数内部的变量会被销毁。但是如果存在其它函数引用这些变量,则这些变量不会被销毁,一直被引用,没有被销毁。

读到这里,则对闭包会有初步的了解。直接上代码分析,如下代码所示:

function f1(){
    var count = 1
    function f2(){
        count ++
        console.log(count);
    }
    return f2
}
let t = f1() //f1()执行的结果就是闭包,因为f1返回了一个函数(f2)
t()
//闭包形成的条件,返回的是一个函数,并且这个函数对局部变量存在引用,维持变量的存在
//因为变量a被f2引用了,所以a不会被销毁,而是被维持着

闭包是一个函数,因此相互直接不会有影响,每个都是私有的。

function f1(){
    var count = 1
    function f2(){
        count ++
        console.log(count);
    }
    return f2
}
let t = f1()
t() //2
t() //3
t() //4
let t2 = f1()
t2() //2
let t3 = f1()
t3() //2
t3() //3

可以见到,这个count并不是2、3、4、5、6、7这样加下去,而是每个实例都是从头开始加,这也证明了闭包之间是互不影响的。

举例说明下闭包的应用

vue中的应用。在最初学习vue时,我们的data都是直接用大括号的,因为我们只是单一组件用它,并不会涉及多个组件同时用这个data。 但是随着后面逻辑越来越复杂,我们会创建多个组件,那么这种大括号形式,数据是不隔绝的(原因是引用地址问题),因此不同组件数据是会有影响的。举个例子

const MyComponent = function() {};
MyComponent.prototype.data = {
    a: 1,
    b: 2,
}
const component1 = new MyComponent();
const component2 = new MyComponent();

component1.data.a === component2.data.a; // true;
component1.data.b = 5;
component2.data.b // 5

这里可以看到,这里的data就是引用数据类型,当修改了component1.data.b时,component2.data.b也会变化。因此,我们的data就变成了如下这种格式,变成了一个函数,这就是一个闭包的应用。

data(){
    return{
        name:'',
        ...
    }
}

实战中闭包具体应用场景

数据的交互,比如封装一个ajax

为什么不选择直接返回一个function,而是选择返回一个对象,主要原因是为了预留空间,更好的扩展。

function f1(){
    let xmlhttp = new XMLHttpRequest()
    return function(){
    }
}

//采用如下方式
function f1(){
    let xmlhttp = new XMLHttpRequest()
    return {
        request:function(){
        }
    }
}
function Http(){
    let xmlhttp = new XMLHttpRequest()
    let _url = ''
    return {
        request:function(method,url,data,success,error){
            xmlhttp.open(method,_url + url)
            if(method == 'get'){
                xmlhttp.send()
            }else{
                xmlhttp.setRequestHeader("Content-Type","application/json")//请求投
                xmlhttp.send(data)
            };
            xmlhttp.onreadystatechange = function(){
                if(xmlhttp.readyState == 4 && xmlhttp.status == 200){
                    success(xmlhttp.responseText)
                }
            }
        }
    }
}

闭包的缺点

  • 不必要的闭包只会徒增内存消耗。
  • 如果引用闭包的函数是一个全局变量,那么闭包会一直存在直到页面关闭;但如果这个闭包以后不再使用的话,就会造成内存泄漏。

使用原则

  • 如果闭包一直使用,则可将其置为全局变量
  • 如果使用频率低,则做为局部变量