1、什么是闭包?
网络异常,图片无法展示
|
如图所示,闭包就是一个定义在函数内部的函数,其作用是将函数内部和函数外部连接起来。大家知道,作用域的问题,就是在函数内部定义的变量称为局部变量,外部取不到值。 下面我们通过代码来更加详细地看一下:
function A() { let x = 1; return function B() { console.log(x); x++; } } console.log(A()); //直接打印返回的B let b = A(); b(); //1 b(); //2 b(); //3
上述代码我们可以看到的是再次执行A
函数的时候(其实这时候执行的是B
),会打印出1
,再次执行,会打印出2
,再执行会打印出3
。我们知道了闭包不仅可以拿到函数内部的变量,还可以保存内部的变量。
接下来,我们来看下闭包的使用场景,
for (var index = 0; index < 5; index++) { setTimeout(function () { console.log(index) }); }
上面的代码段,我们会打印5个5,为啥呢?因为当我们执行for循环时,setTimeout
是异步的,所以每次等for循环加完,再执行setTimeout
函数,一共执行5次。另一个原因因为使用了var
声明的。循环体里的index
跟外部的index
是存在于同一个作用域,相当于在全局定义了一次。
下面我们来使用let
来声明一下。结果就不一样。分别打印0、1、2、3、4
,
for (let index = 0; index < 5; index++) { setTimeout(function () { console.log(index) }); }
最后,我们使用闭包也来实现一下,同样分别打印0、1、2、3、4
,在外部函数每次传入实参时,也就是每次循环的值index
,作用到形参i
,因为setTimeout
为内部函数,每次都会记录值,然后打印出来。
for (var index = 0; index < 5; index++) { (function (i) { setTimeout(function () { console.log(i) }); })(index); }
另一个使用场景是柯里化
。下面一个问题我们将详解。
2、什么是柯里化?
是把接受多个参数的函数变换成接受一个单一参数(最初的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
网络异常,图片无法展示
|
3、柯里化的好处?
- 提高适用性
- 延迟执行
- 提前确认
(1)、提高适用性
我们之前使用函数都是这样写,很冗余。
function check(reg,text) { return reg.test(text); } console.log(check(/\d+/g,'123456')) //true console.log(check(/\d+/g,'abcd')) // false console.log(check(/\d+/g,'r8r8r7')) // true
那么我们通过柯里化的形式简化一下。
function check(reg) { return function (text) { return reg.test(text); } } const checkNum=check(/\d+/g); console.log(checkNum('123456')) //true console.log(checkNum('abcd')) // false console.log(checkNum('r8r8r7')) // true
(2)、延迟执行在需要的时候再执行。
const getResult = add(1)(2)(3); // TODO getResult();
或者
const getResult =add.bind(null,1).bind(null,2).bind(null,3); // TODO getResult();
(3)、提前确认
之前执行一次,就不会每次都去判断了,下面举例:
function getBindEvent(isIE) { if(isIE){ return function () { // TODO console.log(1) } } return function () { // TODO console.log(0) } } const isIE = !!window.ActiveXObject||"ActiveXObject" in window; const bindEvent = getBindEvent(isIE); bindEvent();
最后这里来实现下图所示:
网络异常,图片无法展示
|
function add() { // arguments是一个类数组 const args = Array.prototype.slice.call(arguments); // 将参数都放在一个数组里 return function () { // 下面的arguments与上面的不一样,下面的arguments为当前函数环境下的参数 if(arguments.length){ // 如果有传进来的参数 args.push(...arguments); // 添加到数组里 return arguments.callee; // 返回闭包函数自身 } return args.reduce((n,m)=>{return n+m}); // 否则参数没传进来,就直接执行他 } } console.log(add(1)(2)()); // 3 console.log(add(1,2)(3)(4)()); // 10