函数式编程之闭包与高阶函数

// 执行完这一步 b = inner

b(); // inner 5;

b(); // inner 6;

这就是一个正常的闭包了,我们来分析一下

首先,outer() 代表运行这个函数,并返回结果 inner,这里就是将这个结果赋值给 b。

然后执行 b,其实执行的就是 inner,我们看看执行 inner 会发生什么,首先打印 global,但是函数内部没有啊?那怎么办,其实跟上面一样,刚刚是什么找到 a 的,现在就怎么找 global,只不过多加了一层函数而已,所以这里就在 outer 函数内部找到了 global,那么 outer 函数外部的 global 呢。这里就和原型链一样了,返回的是找到的第一个值。那么对于 a++ 呢,第一次返回 5 也没有问题,第二次呢?一般情况下,如果函数里面的变量没有继续被引用的话就会被销毁,但是我们考虑这么一个情况,还是第一个函数

var a = 1;

function inner(){

a = a + 1;

return a;

}

inner() // 2

console.log(a); // 2

这里的 a 会被销毁吗,不会,为什么,因为 js 主线程一直在运行,所以这个 a 一直存在。对应到上面的 b,正常情况下,outer 函数执行完以后,内部的 global,a,inner 应该都会被销毁的,但是这里为什么没有被销毁呢。我们对比上面的例子。b 引用了外部的 a,outer 内部的 global 变量,所以导致这个变量保存了下来。

用一句简单的话来说,内部函数调用时保存了外部参数的值并能在后续中维持这些参数。当然这么说不严谨,但是这么理解应该没什么问题。

闭包这个函数有如下几个可访问的作用域

  • 在它自身声明之内的变量

  • 全局变量

  • 包含闭包函数的函数里面的变量,这里也就是 a 和 global

如果觉得还是不太清楚的话,可以留言,我后面会详细的讲一下。

4.2 真实的高阶函数(续)

有了对闭包的理解,我们可以实现一些真实有用的高阶函数

4.2.1 tap 函数

由于我们要在函数式编程中处理很多函数,因此需要一种调试方式。

下面实际一个名为 tap 的简单函数。tap 函数接受一个 value 并返回一个包含 value 的闭包函数,该函数将被执行

const tap = (value) => {

(fn) => (

(typeof(fn) === ‘function’ && fn(value)),

console.log(value)

)

}

这里用到了逗号表达式,逗号表达式就是计算逗号前面的值,并返回后面的值,例如

var a = 1;

// 先计算 a++,然后返回 a + 2

var b = (a++,a+2);

console.log(b) // 4

将 tap 函数改成 ES5 的语法即

var tap = function(value){

return function(fn){

return (

// 这句话的意思其实就是如果 fn 是函数的话,计算 fn(value),然后打印 value,否则直接打印 value

(typeof(fn) === ‘function’ && fn(value)),

console.log(value)

)

}

}

// 使用 tap 函数

tap(‘fun’)((it) => console.log(‘value is’,it))

// value is fun

// fun

// 参数不是函数,直接打印 value

tap(‘fun’)()

// fun

在这里分享一个很巧妙的运用逗号表达式的例子

‘helloworld’.split(‘’).reduce((p,k) => ((p[k]++||(p[k]=1)),p),{})

效果如下

1536926915206

按顺序统计出了各个字符的出现次数。为什么呢?首先,我们将它改成非箭头函数

‘helloworld’.split(‘’).reduce(function(p,k){

return (p[k]++||(p[k]=1)),p

},{})

首先,reduce 的第二个参数代表初始值,即这里为 {}。然后进行操作,如果当前字符存在,则 ++,否则则令当前字符为1,即 p[k]++||(p[k]=1),然后就是精髓的逗号,最后实际返回的是 p 对象。以上。

4.2.2 unary 函数

接下来我们看另一个函数

有这么一个例子相信大家都遇到过

[‘1’,‘2’,‘3’].map(parseInt);

// [1,NaN,NaN]

为什么会这样呢,因为 parseInt 接受两个参数,第一个要转换的字符串,第二个是按几进制转换(如果为 0 或者没有默认以 10 进制转换),而这里,map 的第二个参数 index 会默认作为 parse 的第二个参数,所以就产生了上面的结果,那么我们该怎么只传一个参数进去呢?这就是我们这节要讲的 unary 函数

unary 函数接受一个给定的多参数函数,并把它转换成一个只接受一个参数的函数

const unary = (fn) => {

return fn.length === 1 ? fn : (arg)=>fn(arg)

}

我们检查传入的 fn 是否有一个长度为 1 的参数列表,如果有,就什么也不做。如果没有,就返回一个函数,它只接受一个参数 arg,并用该参数调用 fn

让我们试一下

[‘1’,‘2’,‘3’].map(unary(parseInt));

// [1, 2, 3]

这样我们就得到了正确的结果。

4.2.3 once 函数

在很多情况下,我们只需要运行一次给定的函数,比如只想设置一次第三方库,或初始化一次支付设置等。这一节我们将编写一个 once 的高阶函数,它允许开发者只运行一次给定的函数。

const once = (fn) => {

let done = false;

return function(){

return done ? undefined : ((done = true),fn.apply(this,arguments));

}

}

once 函数只接受一个参数 fn 并调用它的 apply 方法,我们声明了一个 done 变量,当第一次调用的时候,使用了逗号表达式将其转换为 true,然后执行 fn 函数。第二次调用时,因为 done 为 true 了,会返回 undefined,阻止了后续的执行,让我们来试一下

var payment = function(){

console.log(‘已付款’);

}

var doPayment = once(payment);

doPayment(); // 已付款

doPayment(); // undefined

最后:

总结来说,面试成功=基础知识+项目经验+表达技巧+运气。我们无法控制运气,但是我们可以在别的地方花更多时间,每个环节都提前做好准备。

面试一方面是为了找到工作,升职加薪,另一方面也是对于自我能力的考察。能够面试成功不仅仅是来自面试前的临时抱佛脚,更重要的是在平时学习和工作中不断积累和坚持,把每个知识点、每一次项目开发、每次遇到的难点知识,做好积累,实践和总结。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

doPayment(); // undefined

最后:

总结来说,面试成功=基础知识+项目经验+表达技巧+运气。我们无法控制运气,但是我们可以在别的地方花更多时间,每个环节都提前做好准备。

面试一方面是为了找到工作,升职加薪,另一方面也是对于自我能力的考察。能够面试成功不仅仅是来自面试前的临时抱佛脚,更重要的是在平时学习和工作中不断积累和坚持,把每个知识点、每一次项目开发、每次遇到的难点知识,做好积累,实践和总结。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

  • 27
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值