拿什么拯救你,我的闭包
一直以为只有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