Web前端最全函数式编程之闭包与高阶函数(1),2024年最新面试Web前端开发十大问题

刷面试题

刷题的重要性,不用多说。对于应届生或工作年限不长的人来说,刷面试题一方面能够尽可能地快速自己对某个技术点的理解,另一方面在面试时,有一定几率被问到相同或相似题,另外或多或少也能够为自己面试增加一些自信心,可见适当的刷题是很有必要的。

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

  • 前端字节跳动真题解析

  • 【269页】前端大厂面试题宝典

最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。大厂面试远没有我们想的那么困难,摆好心态,做好准备,你也可以的。

我们来看上面的定义,闭包就是一个内部函数,所以这里就是指的 inner 函数。那么它的外部函数呢?这里当然指的就是全局执行环境啦,全局执行环境就是 js 代码整个的执行环境,模拟一下

(function global(){

var a = 1;

function inner(){

a = a + 1;

return a;

}

inner() // 2

})()

怎么样,这样就能看出是一个闭包了吧。当然,正常的闭包应该是下面这种方式

let global = ‘outer’

function outer(){

let global = ‘inner’;

let a = 5;

function inner(){

console.log(global)

console.log(a++);

}

return inner;

}

var b = outer();

// 执行完这一步 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]

总结

  • 框架原理真的深入某一部分具体的代码和实现方式时,要多注意到细节,不要只能写出一个框架。

  • 算法方面很薄弱的,最好多刷一刷,不然影响你的工资和成功率😯

  • 在投递简历之前,最好通过各种渠道找到公司内部的人,先提前了解业务,也可以帮助后期优秀 offer 的决策。

  • 要勇于说不,对于某些 offer 待遇不满意、业务不喜欢,应该相信自己,不要因为当下没有更好的 offer 而投降,一份工作短则一年长则 N 年,为了幸福生活要慎重选择!!!

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

喜欢这篇文章文章的小伙伴们点赞+转发支持,你们的支持是我最大的动力!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值