柯里化
面试官看我的简历里有一个:“通过柯里化实现一个请求自动重试函数,一上来就让我写一个无限累加函数。”
我们先看看这个老生常谈的题目。
fn(1)(2)(3)()
// ans = 6
也就是说,我们多次调用,然后最后输出一个 6。
这个题目当然是不难的,我就直接把答案放在这里了。
我当时写第一种方法的时候出了些 bug,当时有一个参数传递错误,导致我 debug 了五分钟没跑出来(
function reduce(...args) {
return args.reduce((x, y) => x + y)
}
function currying(fn) {
let args = []
return function temp(...newArgs) {
if(newArgs.length > 0) {
args = [...args, ...newArgs]
return temp
} else {
const val = fn.apply(this, args)
args = []
return val
}
}
}
const fn = currying(reduce)
console.log(fn(1)(2)(3)())
然后我就立刻换了一种我更熟悉的写法
const sum = (...args) => {
const f = (...newArgs) => sum(...args, ...newArgs)
f.valueOf = () => args.reduce((x, y) => x + y, 0)
return f
}
console.log(sum(1)(2)(3).valueOf())
这样得到答案后面试官就没说啥了,觉得应该是 ok 的。
然后问我柯里化的概念。
这一下让我不清楚了,我没记具体概念,大概说的是“使用闭包实现重复调用。”
面试官没说啥,但我觉得自己表述的并不是很清楚。
柯里化的概念
在计算机科学中,柯里化(英语:Currying ),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数,而且返回结果的新函数的技术。
这个技术由克里斯托弗·斯特雷奇以逻辑学家哈斯凯尔·加里命名的,尽管它是 Moses Schönfinkel 和戈特洛布·弗雷格发明的。柯里化其实也是函数式编程的思想。
柯里化的优点
- 单一原则:在函数式编程中,往往是让一个函数处理的问题尽可能单一,而不是一个函数处理多个任务。
- 提高维护性以及降低代码的重复性
柯里化的使用场景
就是我提到的自动请求重试咯~
当时面试官还让我写了一下我的自动请求重试的伪代码
匆匆忙忙,当时写的大概是这个样子。
function autoRetry(fn, maxTryTime) {
// 初始化请求开始次数
let tryTime = 0
return function tryFunc(...args) {
// 进行请求
try {
fn.apply(this, ...args)
} catch {
if (tryTime < maxTryTime) {
return tryFunc
} else {
// 处理错误
}
}
}
}
深拷贝的循环引用
第三个问题好像就是让我写一个深拷贝,也算常规题目了。
我很快写出来了,提了一嘴 A.A = A 是循环引用
function deepClone(target, map = new WeakMap()) {
if (typeof target === 'object') {
const newTarget = Array.isArray(target) ? [] : {}
if (map.get(target)) return map.get(target)
for(const key in target) {
newTarget[key] = deepClone(target[key])
}
map.set(target, newTarget)
return newTarget
} else {
return target
}
}
然后面试官让我详细解释一下循环引用,然后我蒙住了,A.A = A 不够咩。
他说:“这样解释,我是不懂的。”
我来查查
循环引用是个啥
比如我们有这样的一个对象
我们可以看到循环引用是这样的
我们在进行拷贝的时候,会递归的进行拷贝不停歇,所以会造成循环引用。
所以你看我的代码,有一个 weakMap,他保留了映射关系,再次出现直接用之前的就行,不会一直 deepClone 下去。
那么这就是本次文章的全部内容啦。
感谢阅读~