在我的博客打开开发者工具 -> console,输入 Nlvi.tools.scroll 试试?

开个玩笑,我自己也不知道这算不算柯里化(逃

总之为什么这么写,我自己觉得这样容易理解,方便盲僧(理清)。

还有最近关于“如何治理别人爱装逼的毛病”系列中刚写的一坨,新鲜的:

1
2
3
4
const menuItem = (list) =>
list.map(({title, color, icon, descs}) =>
<menu-item title={title} color={color} icon={icon}>{
descs.map(v => <span>{v}</span>)}</menu-item>)

柯里化是什么?

好了讲柯里化,就是接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。柯里化属于函数式编程的内容

在这里有一个张鑫旭博客讲关于柯里化(柯南嗑药)的例子,很方便理解。当然我想讲点自己的东西,拿Nlvi.tools.scroll说一下吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
tools.scroll = function (win) {
return function(fn) {
$(win).scroll(function() {
var sct = $(win).scrollTop();
fn && fn(sct);
});
};
};
// 实际上如果不作的话,这样就结束了
tools.scroll = function(win, fn) {
return $(win).scroll(function() {
var sct = $(win).scrollTop();
fn&&fn(sct)
})
}

所以函数柯里化看上去有一个特点,会嵌套函数。而且道理就跟柯里化的解释一样:用一些参数,剩下的函数作为返回值里的函数的参数。这样我在调用的时候怎么调用:

1
2
var scrollTop = Nlvi.tools.scroll(window);
scrollTop(function(sct) {});

首先我给个变量(实际上ES6直接用const,毕竟固定量)引用这个方法,方法传入第一个参数。接着里面的函数返回值函数是接收一个函数变量。

理一下,函数 - 的返回值 - 是个函数,这个函数接收一个 - 函数 - 的 - 变量。

然后这个函数参数传进去之后,实际上就是传一个方法进去以便于我等下里面的逻辑走完之后可以把这个函数参数运行起来,行程一个特别做作的回调函数。(当然回调函数带的是当前滚动条的数据)

为什么这么做呢?一点就是我觉得这样思路就清晰多了,而且在调用区域写起来干净。第二点就是参数复用。

虽然这个例子看不出什么,但是难免会遇到一些情况,就是明明一个变量或者一个参数,我只要取一次然后调用一次之后,剩下的内容我只要在这个情况之上去处理就行。但是如果传统单函数的做法的话,就会使得这个局外参数也跟着在里面做一些乱七八糟的运动,比如跟着去递归,被扯着去多次判断,这样就会莫名其妙浪费了一堆性能。很简单我举个例子:4x100,明明你交接之后剩下的就接下来的人跑就行了,你非得交接之后跟着跑,边跑边喊加油?

第三点就是延时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var total = 0
// normal
var nSum = (num) => {
total += num
}

//currying
var cSum = (fn) => {
var nums = []
return function() {
if (!arguments.length) {
return fn.apply(null, nums)
}
Array.prototype.push.apply(nums, arguments)
return this
}
}

var sum = cSum((x) => total += x)

如果是常规的话,那么肯定会立刻计算出结果,而柯里化之后,会把计算的步骤储存起来,然后一起计算。

柯里化误区

在写这篇东西的时候,我关注到了一尊大神的博客 JavaScript函数柯里化的一些思考,吓得我赶紧回看了自己的函数有没有“画蛇添足”的成分。嗯。

1
2
3
// 拿下来继续学习一下
var scrollTop = Nlvi.tools.scroll(window);
scrollTop(function(sct) {});

这里我想到的是,我只需要把window传到这个函数之后引用出一个新函数,然后我就可以带着这个函数跑了,如果是不嫌麻烦版本的话我需要则么做?

1
2
3
4
5
6
7
8
tools.scroll = function(win, fn) {
return $(win).scroll(function() {
var sct = $(win).scrollTop();
fn&&fn(sct)
})
}
// and so
tools.scroll(window, function() {})

这样就会造成,我每次想监听滚动的时候,每用一次这个方法,我就要把window传进去一次。如果包一下的话,我就可以拿后面那个函数走就行了,也就是我的window只要传一次就畅通无阻。这也是另一个好处:分段计算。好比我玩Switch,我在外面突然想玩马车的时候,如果我的卡带在卡包里,那我换上就可以玩了,就不需要等回到家把卡带从包装盒里拿出来装到机器上。

实际上到这里的话应该有所经验了,到这里还会不会觉得apply(), call(), bind()使用机会少之又少?这就是数学的魅力吧,可是我数学不好。

柯里化 < - > 闭包函数?

又是每期一问了,柯里化跟闭包函数是否也有关系?

1
2
3
4
5
6
7
8
// 经典面试题
for(var i=0;i<2;i++){
(function(i){
setTimeout(function(){
console.log(i);
},0)
})(i);
}

那么,柯里化函数和闭包函数应该怎么区分?还是的确有联系?

最后感谢所有被引用的文章及其作者,感谢帮助学习。