什么是闭包?闭包的应用有哪些?

是不是很多人一开始所了解的闭包是一个函数套着另一个函数,然后里面的函数可以引用外面的函数的变量(很久之前俺就是这么理解的)?看完这篇文章后,球球泥们不要再这么回答了好嘛!(憨憨叉腰)
下面先来看看闭包是啥,它有啥用呢?

闭包及其作用

闭包:它是函数运行时所产生的机制,函数执行会形成一个全新的私有上下文,可以保护里面的私有变量和外界互不干扰,这就是所谓的保护机制!如果当前上下文不被出栈释放,这样私有变量及它的值也不会被释放掉,这就是保存机制
所以闭包的作用是:保存&保护

简单来说,函数执行会形成一个私有上下文,如果私有上下文中的某些内容被私有上下文以外的其他事物所占有,当前上下文就不能被出栈释放,那么它里面的私有变量就被保存起来了。同时,在代码执行过程中,它里面的私有变量和外界互不干扰,我对私有变量的一些操作只是操作私有的,不影响外界的变量值,这种情况叫闭包。

我看到有篇博客里写的特别搞笑且通俗易懂,说闭包就是你的老婆出轨隔壁老王了, 老王可以很轻易的通过你老婆了解你家里的情况, 甚至指示她改变你的家, 只要你还没离婚,这种状况就一直会持续下去……

闭包的应用

聊完了闭包的作用,那么它的应用有哪些呢?你如果只简单的学习了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