开头放个柯里化的通用方法
// 支持多参数传递
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);
}
}
定义
-
把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术
-
解释:只传递一个参数来调用函数,返回一个函数来接收剩余参数并处理结果(也被称为部分求值)
- 原函数--接收4个参数,return 结果
- 柯里化--接收1个参数,return一个函数
- 此函数接收剩余参数并return最终结果
-
例子:将原本传递x、y的函数变成两个函数,外层传递x内层传递y
function curryingAdd(x) {
return function (y) {
return x + y
}
}
应用
(1)参数复用
//正常方法
function meet(msg, name) {
console.log(name, msg, "!");
}
meet("hello", "Bob");
//柯里化封装
function meetCur(msg) {
return function (name) {
console.log(name, msg, "!");
};
}
//获得自动填充hello的方法
let helloMeet = meetCur("hello");
helloMeet("Lucy");
helloMeet("Bob");
helloMeet("Jack");
//获得自动填充goodBye的方法
let byeMeet = meetCur("goodBye");
byeMeet("Lucy");
byeMeet("Bob");
byeMeet("Jack");
(2)提前判断
原本每次调用on都要判断是否支持addEventListener,
柯里化后在赋值时确认后就不需要再每次判断
var on = function(element, event, handler) {
if (document.addEventListener) {
if (element && event && handler) {
element.addEventListener(event, handler, false);
}
} else {
if (element && event && handler) {
element.attachEvent('on' + event, handler);
}
}
}
var on = (function() {
if (document.addEventListener) {
return function(element, event, handler) {
if (element && event && handler) {
element.addEventListener(event, handler, false);
}
};
} else {
return function(element, event, handler) {
if (element && event && handler) {
element.attachEvent('on' + event, handler);
}
};
}
})();
(3)bind实现
<script>
Function.prototype.mybind = function (context) {
let that = this;
// console.log("tha1t", that);
let args = Array.prototype.slice.call(arguments, 1);
console.log(args, "args");
return function () {
// console.log(that, "--that", str);
let arr = Array.prototype.concat.apply(args, arguments);
return that.apply(context, arr);
};
};
function tt(...str) {
// console.log(this, "------", str);
return str;
}
let ll = console.log;
console.log(tt.mybind(this, 234, 234, 43)("0--00"));
ll.mybind(this, 234, 234, 43)("0--00")
</script>
(4)延迟执行
下面代码,只要持续传入参数就不执行处理结果而是接收参数并返回函数。
当不再传入参数时就将之前接收的所有参数用于执行,返回执行结果
var curryScore = function (fn) {
var allScore = []; //用来存取每次输入的单个值
// 这些用来预处理
return function () {
if (arguments.length === 0) {
//不再传入参数时执行fn
fn.apply(null, allScore);
} else {
//传入参数则保存起来
allScore = allScore.concat([].slice.call(arguments));
}
};
};
var result = 0;
// addScroe得到的是一个function
var addScore = curryScore(function () {
//fn
for (var i = 0; i < arguments.length; i++) {
result += arguments[i];
}
});
addScore(3);
console.log(result); //0
addScore(3);
console.log(result); //0
addScore(3);
console.log(result); //0
addScore();
console.log(result); //9
封装通用方法
- 完整的,通用的,将一个函数封装柯里化
解析
// 支持多参数传递
function progressCurrying(fn, args) {
var _this = this
var len = fn.length; //fn声明中的形参数量
var args = args || []; //没有传args则设为[]
return function() {
//获取之后传入的参数,将参数整入数组
var _args = Array.prototype.slice.call(arguments);
//将之前传入的参数与这次传入的参数整合
Array.prototype.push.apply(args, _args);
// 如果参数个数小于最初的fn.length,则递归调用,继续收集参数
if (_args.length < len) {
//即return的仍然是一个可加()执行的。
return progressCurrying.call(_this, fn, _args);
}
// 参数收集完毕,则执行fn
return fn.apply(this, _args);
}
}
练习题
- 简化题
//实现一个add方法,使其实现下面结果
//(将网上那道面试题简化了一下)
console.log(add(1)(2)()); //3
console.log(add(1, 2, 3)(4)()); //10
console.log(add(1)(2)(3)(4)(5)()); //15
function add(...args) {
let ret = [];
ret.push(...args);
if (args.length == 0) return adder();
function adder(...arg2) {
ret.push(...arg2);
if (arg2.length === 0) {
let sum = 0;
ret.forEach((item) => {
sum += item;
});
return sum;
} else {
return adder;
}
}
return adder;
}
- 原题
// 实现一个add方法,使计算结果能够满足如下预期:
// add(1)(2)(3) = 6;
// add(1, 2, 3)= 6;
// add(1)(2,3)= 6;
function add() {
var _args = Array.prototype.slice.apply(arguments);
// 每次_add传入的参数都保存到_args中
// 这里体现了 柯里化 参数复用 (_args)与 延迟执行(等到所有参数收集完毕再进行计算)的两大特点
var _add = function () {
Array.prototype.slice.apply(arguments).forEach(function (item) {
// 另一个特性,在最终结算结果之前提前确认参数,
if (!isNaN(item)) {
_args.push(item);
}
});
return _add;
};
// 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
//这一步解决同时可以加()执行也可以输出结果问题
_add.toString = function () {
return _args.reduce(function (x, y) {
return x + y;
});
};
return _add;
}