欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

什么是闭包?闭包有啥作用?闭包的应用有啥?

程序员文章站 2021-12-07 08:21:38
闭包,是函数运行时所产生的机制!...

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

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

闭包及其作用

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

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

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

闭包的应用

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