likes
comments
collection
share

拿什么拯救你,我的闭包

作者站长头像
站长
· 阅读数 12

一直以为只有Python有闭包,直到和前端小伙伴聊了面试才发现,不是,前端也有,而且十分相似,并且,有同样的特质,那么我们就来聊聊吧。

面试

还是A哥的面试,嘿嘿嘿。

面试官:了解闭包吗?

A哥:当然啦,就是函数嵌套函数,外层函数返回内层函数。

面试官(嘴角上扬):还有吗?

A哥:嗯,他很好用。

面试官(-_-!):可以和我说一下你在工作当中使用闭包的场景吗?

A哥:都可以用啊,一般是在给个性代码添加共性的时候,比如,给所有的输入添加长度校验。

面试官(^_^): 嗯,好的......

后来,A哥也没有收到这家公司的二面,老哥哥面色通红,和我们嘟囔着什么,面试造航母,工作没鸟用啥的,整个屋子里充满了快乐的气氛。然后作为Python程序员的我,经常使用装饰器函数,自然就查了查,发现JS闭包和Python的很相似,所以就做了一下分享。

闭包

聊闭包,不得不聊一下下面的概念:

1、作用域

作用域就是变量起作用的范围,再通俗的说就是可以调用到这个变量的范围,一般分为全局和局部,当然不同的语言还有各自的细分,比如,python出来全局和局部还是内置和嵌套。

那为啥要在闭包的时候聊作用域呢,因为,JS函数当中的变量只在函数内部可以调用,JS函数当中的变量的作用域是函数内部的局部作用域,而闭包本身就是在函数内部嵌套一个函数,然后返回嵌套的函数,那么要想调用到嵌套在内部的函数,就只能在外部调用外层函数,期待他返回了。

<script>
    function outer() {
        function inner() {
            console.log("I am inner");
        }
        console.log(inner);
        return inner //注意,这里返回的是inner函数本身,并没有调用inner函数
    }
    // inner(); //这里直接调用inner是调用不到的 返回 cannot access 'inner' before initialization

    const inner = outer(); //调用inner需要调用outer返回inner函数 调用outer,也就调用console.log(inner),返回inner函数 ƒ inner() {console.log("I am inner");}
    
    inner() //然后再次调用  返回I am inner
</script>

所以,闭包到底有啥用呢,只是这样的嵌套吗,并不是,专业角度上讲:

(1)调用outer函数的时候,outer函数当中的内容会放到内存当中(包括inner函数),那么下次调用的时候可以直接从内存当中获取inner,这样形成一种懒加载的形式。

(2)从代码结构上看,像上面A哥说的那样,可以把共性的代码存放到outer一层,提高代码的复用率和一致性。

比如:

<script>
    function outer(fun,args) {
        function inner() {
            if(args){
                return fun(args)
            }else {
                return {"message": "该参数为空", "code": -1, "data": {}}
            }
        }
        return inner

    }

    function fun1(args) {
        return args
    }

    function fun2(args) {
        return args
    }
    //将函数和参数赋值进去,这样就不用考虑判断的代码了,判断的逻辑集中在outer当中就可以了
    var fun1 = outer(fun1,"");
    var fun2 = outer(fun2,{"name": "hello world"});

    console.log(fun1()); // {message: '该参数为空', code: -1, data: {…}}
    console.log(fun2()) // {name: 'hello world'}
</script>

上面的代码被A哥形容是py里py气的,但是也说明了他要说明的道理,当然还用很多使用场景,比如大家熟悉的节流,迭代器,函数回调都是可以的。

2、垃圾回收机制

所谓成也风云,败也风云

拿什么拯救你,我的闭包

JS内存回收采用垃圾回收机制,会把没有引用的变量回收回去。

但是闭包内部函数引用外部的函数的变量,外部函数执行完毕,作用域也不会删除。从而形成了一种不删除的独立作用域。把内部的变量都放到内存当中,于是内存消耗变得很大,如果滥用闭包会造成网页的性能问题,嘿嘿嘿,如果在IE当中可能导致内存泄露哦。

所以我们需要在退出函数前,把没有用的局部变量都手动的删除掉。按照垃圾回收机制的规则,变量设置为null即可。当然不可以直接设置,因为js当中还有一个循环引用的问题,所以可以设置一个对象。

function outer(fun,args) {
    var obj = {"args": args};
    var b = obj.args; //解除循环引用
    function inner() {
        if(b){
            return fun(b)
        }else {
            return {"message": "该参数为空", "code": -1, "data": {}}
        }
    }
    obj = null; //obj置空,将闭包引用的外部函数中活动对象obj.args清除
    return inner
}

关于闭包就聊这么多,欢迎给位大佬多多指点。

转载自:https://juejin.cn/post/7222297927438614586
评论
请登录