Open YvetteLau opened 5 years ago
const currying = fn => { if (typeof fn !== 'function') { return new Error('No function provided') } return function curriedFn(...args){ if (args.length < fn.length) { return function () { return curriedFn.apply(null, args.concat([].slice.call(arguments))) } } return fn.apply(null, args) } }
再来个左向, 自动柯里化
var currying = function (fn) {
if (typeof fn !== 'function') {
return new Error('need function')
}
var args = []
return function doCurrying() {
if (arguments.length === 0) {
return fn.apply(this, args);
} else {
[].push.apply(args, arguments);
return doCurrying;
}
}
}
function curry(func, arity = func.length, guard, partial = []) {
// guard 守卫, 防止传入多余参数
partial = guard === void 0 ? partial : []
const placeholder = curry.placeholder
const boundFunc = function(...args) {
const argsLen = args.filter(arg => arg !== placeholder).length
// _replaceHolders 函数, 处理占位符的情况
const finalArgs = _replaceHolders(partial, args, placeholder)
if (argsLen >= arity) {
// _executeBound 函数, 处理 new 调用boundFunc ,this应被忽略问题
return _executeBound(func, boundFunc, this, this, finalArgs)
} else {
return curry(func, arity - argsLen, void 0, finalArgs)
}
}
return boundFunc
}
// 设置占位符, 默认 '__'
curry.placeholder = '__'
// 整合 partials 和 args 为一个 完整的参数数组, 将partials中的 placeholder替换为 args中元素, args中剩余元素放到 数组结尾
function _replaceHolders(partials, args, placeholder) {
let separator = 0
const combinedArgs = partials.map(partial => {
if (partial === placeholder) {
return args[separator++]
}
return partial
})
return [...combinedArgs, ...args.slice(separator)]
}
function _executeBound(func, boundFunc, thisArg, context, args) {
if (!(context instanceof boundFunc)) {
return func.call(thisArg, ...args)
}
const instance = Object.create(func.prototype)
const result = func.call(instance, ...args)
if (isObject(result)) {
return result
}
return instance
}
function isObject(obj) {
const type = typeof obj
return obj !== null && (type == 'object' || type == 'function')
}
函数柯里化是逐步传参、逐步缩小函数的适用范围、逐步求解的过程,它将多变量函数拆解为单变量的多个函数的依次调用。
var currying = function (fn) {
var args = [];
return function () {
if (arguments.length === 0) {
return fn.apply(this, args);
} else {
[].push.apply(args, arguments);
return arguments.callee;
}
}
};
函数柯里化的主要作用和特点:
const currying = fn => {
return function curriedFn(...args){
if (args.length < fn.length) {
return function () {
return curriedFn.apply(null, args.concat([].slice.call(arguments)))
}
}
return fn.apply(null, args)
}
}
function curry(fn) {
let oldArgs = Array.prototype.slice.call(arguments, 1);
return function() {
let newArgs = Array.prototype.slice.call(arguments);
let args = newArgs.concat(oldArgs);
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return curry.call(this, fn, ...args);
}
}
}
在开始之前,我们首先需要搞清楚函数柯里化的概念。
函数柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
const currying = (fn, ...args) =>
args.length < fn.length
//参数长度不足时,重新柯里化该函数,等待接受新参数
? (...arguments) => currying(fn, ...args, ...arguments)
//参数长度满足时,执行函数
: fn(...args);
function sumFn(a, b, c) {
return a + b + c;
}
var sum = currying(sumFn);
console.log(sum(2)(3)(5));//10
console.log(sum(2, 3, 5));//10
console.log(sum(2)(3, 5));//10
console.log(sum(2, 3)(5));//10
函数柯里化的主要作用:
函数柯里化,就是见一个接受多个参数的函数转化为接受单一参数的函数的技术.代码如下,亲测可用
const cury = function (fn) {
const args = Array.prototype.slice.call(arguments, 1)
const argArr = [...args]
return function currying() {
argArr.push(...arguments)
if (argArr.length < fn.length) {
return cury.call(this, fn, ...argArr)
} else {
return fn.call(null, ...argArr)
}
}
}
function add(a, b, c) {
return a+ b+ c
}
const curyAdd = cury(add)
console.log(curyAdd(1)(2,3))
柯里化函数:一个currying的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值。 特点:1.延迟计算 2.参数复用 3.动态生成函数的作用
var curryings = function(fn){
var args = [];
//存储到curring函数中除了fn之外的其它参数,并存储args函数中
args = args.concat([].slice.call(arguments,1));
return function(){
if(arguments.length===0){
return fn.apply(this,args);
}else{
//将fn中的参数展开,然后在存储到args数组中
[].push.apply(args,arguments);
}
}
}
var costss = (function(){
var money = 0;
return function () {
for (var i = 0, l = arguments.length; i < l; i++) {
money += arguments[i]
}
return money;
}
})()
var costst = curryings(costss,100,200)// 转化成 currying 函数
costss(100,200)
console.log(costst()) //600
/**
* 利用闭包的特性让参数暂时保存,让传入的参数与原函数所需参数数量进行比较,未达到则递归进行存储,待达到则一次性执行
* @param {*} fn 需要被柯里化的函数
* @param {...any} oldArgs 所存入的闭包的参数值
*/
const currying = (fn, ...oldArgs) => (...newArgs) => {
let args = [...oldArgs, ...newArgs];
if (args.length >= fn.length) {
return fn(...args);
} else {
return currying(fn, ...args);
}
};
function sumFn(a, b, c) {
return a + b + c;
}
var sum = currying(sumFn);
console.log(sum(1)(2)(3));
console.log(sum(1, 2)(3));
console.log(sum(1, 2, 3));
const progressCurrying=(fn,...args) =>{
var len = fn.length
//var args = args || []
return function() {
var _args = Array.prototype.slice.call(args)
_args.push(...arguments)
// 如果参数个数小于最初的fn.length,则递归调用,继续收集参数
if (_args.length < len) {
return progressCurrying.call(this,fn,...args,..._args)
}else{
// 参数收集完毕,则执行fn
return fn.apply(this, _args)
}
}
}
let test = progressCurrying(function(a, b, c) {
console.log(a + b + c)
})
test(1, 2, 3)
test(45, 12)(13)
test(11)(21)(31)
curry 的概念很简单:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。 你可以一次性地调用 curry 函数,也可以每次只传一个参数分多次调用。
例子:
var add = function(x) {
return function(y) {
return x + y;
};
};
var increment = add(1);
var addTen = add(10);
increment(2);
// 3
addTen(2);
// 12
1- 这里利用的是bind的this参数后面的会使一个函数拥有预设的初始参数。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置
var fn = function () {
return fn.bind(null, ...aruguments)
}
2- 在1的基础上限制参数的个数,比如你想要的是5个
var numOfRequiredArguments = 5;
var fn = function() {
if (arguments.length < numOfRequiredArguments) {
return fn.bind(null, ...arguments);
} else {
console.log('we already collect 5 arguments: ', [...arguments]);
return null;
}
}
存在的问题:
1. 我们希望将收集到的参数传递给需要它们的目标函数,而不是通过将他们传递给console.log()打印
2. 变量numOfRequiredArguments不应该是写死的,它应该是目标函数所期待的参数个数
3- 因此,除去存储参数以外,我们还需要在某处存储对于目标函数的引用
function magician(targetfn) {
var numOfArgs = targetfn.length;
return function fn(){
if (arguments.length < numOfArgs) {
return fn.bind(null, ...arguments);
} else {
return targetfn.apply(null, arguments);
}
}
}
magician 函数的作用是:它接收目标函数作为参数,然后返回‘参数收集器’函数,与上例中 fn 函数作用相同。唯一的不同点在于,当收集的参数数量与目标函数所必需的参数数量相等时,它将把收集到的参数通过 apply 方法给到该目标函数,并返回计算的结果。这个方法通过将其存储在 magician 创建的闭包当中来解决第一个问题(引用目标函数)。
4- 使用magician函数本身作为参数收集器
function magician(targetfn) {
var numOfArgs = targetfn.length;
if (arguments.length - 1< numOfArgs) {
return magician.bind(null, ...arguments)
} else {
return targetfn.apply(null, Array.prototype.slice.call(arugments, 1))
}
}
因为magician接受目标函数作为它的第一个参数,因此收集到的参数将始终包含该函数作为arguments[0]。这就导致,我们在检查有效参数的总数时,需要减去第一个参数。 因为目标函数时递归传递给magician函数的,因此我们可以通过传入第一个参数显示地引用目标函数,以代替使用闭包保存目标函数的引用
function progressCurrying(fn, args) {
var _this = this
var len = fn.length;
var args = args || [];
return function() {
var _args = Array.prototype.slice.call(arguments);
Array.prototype.push.apply(args, _args);
// 如果参数个数小于最初的fn.length,则递归调用,继续收集参数
if (_args.length < len) {
return progressCurrying.call(_this, fn, _args);
}
// 参数收集完毕,则执行fn
return fn.apply(this, _args);
}
}
const sum = (...args) => args.reduce((acc, cur) => acc + cur, 0)
const add = currying(sum, 2)
console.log(add(1)(2))
常规法
支持多调用法