# 如何在JavaScript中使用Currying和Composition

## 咖喱，任何人？

``````const distance = (start, end) => Math.sqrt( Math.pow(end.x-start.x, 2) + Math.pow(end.y-start.y, 2) );

console.log( distance( {x:2, y:2}, {x:11, y:8} );
// logs 10.816653826391969

``````const distance = function(start){
// we have a closed scope here, but we'll return a function that
//  can access it - effectively creating a "closure".
return function(end){
// now, in this function, we have everything we need. we can do
//  the calculation and return the result.
return Math.sqrt( Math.pow(end.x-start.x, 2) + Math.pow(end.y-start.y, 2) );
}
}

console.log( distance({x:2, y:2})({x:11, y:8});
// logs 10.816653826391969 again

``````const distancewithCurrying =
(start) =>
(end) => Math.sqrt( Math.pow(end.x-start.x, 2) +
Math.pow(end.y-start.y, 2) );

``````const players = [
{
name: 'Alice',
color: 'aliceblue',
position: { x: 3, y: 5}
},{
name: 'Benji',
color: 'goldenrod',
position: { x: -4, y: -4}
},{
name: 'Clarissa',
color: 'firebrick',
position: { x: -2, y: 8}
}
];
const flag = { x:0, y:0};

``````// Given those, let's see if we can find a way to map
//  out those distances! Let's do it first with the first
//  distance formula.
const distances = players.map( player => distance(flag, player.position) );
/***
* distances == [
*   5.830951894845301,
*   5.656854249492381,
*   8.246211251235321
* ]
***/

// using a curried function, we can create a function that already
//  contains our starting point.
const distanceFromFlag = distanceWithCurrying(flag);
// Now, we can map over our players to extract their position, and
//  map again with that distance formula:
const curriedDistances = players.map(player=>player.position)
.map(distanceFromFlag)
/***
* curriedDistances == [
*   5.830951894845301,
*   5.656854249492381,
*   8.246211251235321
* ]
***/

## 如何组合咖喱函数

``````// a source array,
const ages = [11, 14, 26, 9, 41, 24, 108];
// a filter function. Takes an input, and returns true/false from it.
function isEven(num){
if(num%2===0){
return true;
} else {
return false;
}
}
// or, in ES6-style:
const isEven = (num) => num%2===0 ? true : false;
// and applied:
console.log( ages.filter(isEven) );
// [14, 26, 24, 108]

``````// we write a function that takes in a value...
function isEqualTo(comparator){
// and that function *returns* a function that takes a second value
return function(value){
// and we simply compare those two values.
return value === comparator;
}
}
// again, in ES6:
const isEqualTo = (comparator) => (value) => value === comparator;

``````const isEqualTo = (comparator) => (value) => value === comparator;
const isGreaterThan = (comparator) => (value) => value > comparator;

// and in application:
const isSeven = isEqualTo(7);
const isOfLegalMajority = isGreaterThan(18);

``````// function to simply invert a value: true <=> false
const isNot = (value) => !value;

const isNotEqual = (comparator) => (value) => isNot( isEqual(comparator)(value) );
const isLessThanOrEqualTo = (comparator) => (value) => isNot( isGreaterThan(comparator)(value) );

``````// all of the building blocks...
const isGreaterThan = (comparator) => (value) => value > comparator;
const isNot = (value) => !value;
const isLessThanOrEqualTo = (comparator) => (value) => isNot( isGreaterThan(comparator)(value) );

// simply to get this?
const isTooYoungToRetire = isLessThanOrEqualTo(65)

// and in implementation:
const ages = [31, 24, 86, 57, 67, 19, 93, 75, 63];
console.log(ages.filter(isTooYoungToRetire)

// is that any cleaner than:
console.log(ages.filter( num => num <= 65 ) )

"在这种情况下，最终的结果是非常相似的，所以它并没有真正为我们节省什么。事实上，考虑到前三个函数中的设置，它比简单地做比较要花费更多时间来构建！"

• 首先，我们正在编写的代码更具有_自我记录_性。通过使用富有表现力的函数名称，我们能够一眼就看出我们正在过滤`ages` ，以获取数值`isTooYoungToRetire` 。我们不是在看数学，而是在看描述。
• 第二，通过使用非常小的原子函数，我们能够孤立地测试每一块，确保它每次的表现都完全相同。以后，当我们重复使用这些小函数时，我们可以确信它们会起作用--当我们的函数复杂度增加时，我们就不用再测试每个小部分了。
• 第三，通过创建抽象函数，我们以后可能会在其他项目中找到它们的应用。建立一个功能组件库是一个非常强大的资产，我强烈建议培养这个资产。

``````const isInRange = (minComparator)
=> (maxComparator)
=> (value) => isGreaterThan(minComparator)(value)
&& isLessThan(maxComparator)(value)

const isTwentySomething = isInRange(19)(30);

``````const and = (conditions) =
(value) => conditions.every(condition => condition(value) )

const isInRange = (min)
=>(max)
=> and([isGreaterThan(min), isLessThan(max) ])