什么是闭包?闭包有啥作用?闭包的应用有啥?
什么是闭包?闭包的应用有哪些?
是不是很多人一开始所了解的闭包是一个函数套着另一个函数,然后里面的函数可以引用外面的函数的变量(很久之前俺就是这么理解的)?看完这篇文章后,球球泥们不要再这么回答了好嘛!(憨憨叉腰)
下面先来看看闭包是啥,它有啥用呢?
闭包及其作用
闭包:它是函数运行时所产生的机制,函数执行会形成一个全新的私有上下文,可以保护里面的私有变量和外界互不干扰,这就是所谓的保护机制!如果当前上下文不被出栈释放,这样私有变量及它的值也不会被释放掉,这就是保存机制!
所以闭包的作用是:保存&保护。
简单来说,函数执行会形成一个私有上下文,如果私有上下文中的某些内容被私有上下文以外的其他事物所占有,当前上下文就不能被出栈释放,那么它里面的私有变量就被保存起来了。同时,在代码执行过程中,它里面的私有变量和外界互不干扰,我对私有变量的一些操作只是操作私有的,不影响外界的变量值,这种情况叫闭包。
我看到有篇博客里写的特别搞笑且通俗易懂,说闭包就是你的老婆出轨隔壁老王了, 老王可以很轻易的通过你老婆了解你家里的情况, 甚至指示她改变你的家, 只要你还没离婚,这种状况就一直会持续下去……
闭包的应用
聊完了闭包的作用,那么它的应用有哪些呢?你如果只简单的学习了js的一些基础语法,想继续提升一下,那么就请麻溜的看下去!
1、单例设计模式
单例设计模式:用单独的实例来管理当前事物的属性和方法(可以理解为产生一个类的唯一实例),基于闭包管控的单例设计模式称为高级单例设计模式,以此来实现模块划分,这也是最早的模块化思想。
看下面的代码就是用闭包思想实现的:
let moduleA = (function(){
function AAA(){};
function BBB(){};
return {
A //如果想要把需要供外面访问的变量和方法,那就写在return中
}
})();
moduleA.AAA(); //调用模块A中的AAA方法
你看出闭包思想怎么体现了吗?你瞅瞅,这个moduleA 的方法AAA在外面都被调用了,那moduleA 占用的内存能释放不能?铁定不能啊!早期多人开发的时候,好多人起一样的变量名,会造成变量污染的,然后就用这种方法好了很多!
2、惰性函数
惰性函数:函数执行的分支只会在函数第一次调用的时候执行,在第一次调用过程中,该函数会被覆盖为另一个按照合适方式执行的函数,下次执行的时候就执行覆盖后的函数。
惰性思想,就是懒呗,执行过一次的东西,如果第二部执行还是一样的效果,那就不要重复完整的执行第二遍了。
举一个栗子!假设你想获取浏览器中的样式,你能用啥方法?考虑兼容呢?
·getComputedStyle方法,该属性是兼容火狐谷歌,不兼容IE6-8!
·currentStyle方法,该属性只兼容IE,不兼容火狐和谷歌!
·style 更不行了哇!只能获取行内样式(就是写在html标签里的)!
好,咱们来考虑下怎么在兼容浏览器的情况下获取样式,首先用普通方法来写:
function getStyle(element, cssName){
if('getComputedStyle' in window) //in 判断某个属性是否为当前对象的属性,就是找window里有没有getComputedStyle属性,如果有,那就是谷歌或者火狐等浏览器,就执行下面这个方法
return window.getComputedStyle(element)[cssName];
//没有getComputedStyle就是ie浏览器呗,就执行currentStyle方法,
return element.currentStyle[cssName];
}
getStyle(document.body,'margin')
没毛病吧?下面看看用惰性思想来实现的代码:
function getStyle(element, cssName){
if('getComputedStyle' in window){ //in 判断某个属性是否为当前对象的属性
getStyle = function(element, cssName){
return window.getComputedStyle(element)[cssName];
}
}
else{
getStyle = function(element, cssName){
return element.currentStyle[cssName];
}
}
return getStyle(element, cssName) //为了第一次执行下面的获取margin样式也能拿到window下的样式。
}
getStyle(document.body,'margin')
getStyle(document.body,'padding')
getStyle(document.body,'height')
用惰性思想改完后的代码的getStyle是一个全局函数,在浏览器关闭时才会释放,里面的function被返回给了getStyle,所以形成了一个闭包。
咱代码第一次执行的时候,如果遇到的有getComputedStyle属性,那就是谷歌它们呗,和上面没用惰性思想的代码相比,就是把function匿名函数重新赋值给getStyle了,接下来都在谷歌浏览器执行这个方法了,就不需要再判断一次了!多省事!
懂了后做做下面的题呗?看看输出啥?这题也是惰性思想的体现!
3、柯里化函数
柯里化函数思想:利用闭包保存机制,把一些信息预先存储起来(预处理的思想)
首先问一个问题,这也是百度问过的一题:
实现函数fn,让其具有如下功能:
该怎么写呢?是不是可以把fn(1,2)先执行了,执行后的返回值传入实参3继续执行呢?答案如下:
function fn(...outerArgs){
return function(...innerArgs){
let args = outerArgs.concat(innerArgs); //args:将外层和里层传递的所有值都合并在一起
return args.reduce( (n,item) => n+item );
};
}
//——————
let res = fn(1,2)(3);
console.log(res) //输出6
//————下面的就相当于:
let f = fn(1,2);
console.log(f(3)); //输出6
4、compose函数
compose函数:组合式函数,把多层函数嵌套调用扁平化!
我们又要来看一个例题了,毕竟实践是最好的老师~
又是来自百度的面试题:
实现函数fn,让其具有如下功能:
使用compose:利用柯里化思想存储预处理的值,然后利用reduce函数遍历每一个函数,把上一次函数执行的返回值作为实参传递给下一个函数执行。答案如下:
function compose(...funcs){ //funcs:存储按照顺序执行的函数(或函数数组,例如【fn1,fn2,fn3】)
return function(...args){ //args:存储第一个函数执行需要传递的实参信息(数组)-->20
if(funcs.length===0) return args;
if(funcs.length===1) return funcs[0](...args);
return funcs.reduce((N,func) => {
//第一次N的值:是第一次函数执行的实参,func是第一个函数
//第二次N的值:上一次func执行的返回值,作为实参传递给下一个函数执行
return Array.isArray(N)?func(...N):func(N); //说到这,问个题外问题:判断是数组的方法有哪些?
},args);
};
}
let res = compose(fn1,fn2,fn3,fn4)(20);
console.log(res);
还有很多地方也用到了闭包机制,比如redux源码里~那就是后话了!
闭包的优缺点
优点:就是它的作用,保护和保存。
缺点:闭包会产生不销毁的上下文,会导致栈/堆内存消耗过大,有时候也会导致内存泄漏等,影响页面的运行性能,所以在真实项目中,要合理应用闭包!
完结撒花
如果你觉得这篇文章对你有用的话,那就请点个赞赞~~~~~
本文地址:https://blog.csdn.net/Dracolan/article/details/107490288