高级技巧(2)——函数柯里化
函数柯里化
-
复习
2、addHandler (),他的职责是分情况使用DOM0级方法、DOM2级方法或IE方法来添加事件。这个方法属于一个名字叫EventUtil的对象,可以使用这个对象来处理浏览器间的差异。addHandler() 方法接受3个参数:要操作的元素、时间名称和事件处理程序函数
。
3、removeHandler(),它也接受相同参数。这个方法的指责是移除之前添加的事件处理程序,无论该事件处理程序是采取什么方式添加到元素中的。如果其他方法无效,默认采用DOM0级方法
。 -
概念:
-
一个简单的引例:
// 普通的add函数
function add(x, y) {
return x + y
}
// 柯里化以后
function curryingAdd(x) {
return function (y) {
return x + y
}
}
add(1, 2) // 3
curryingAdd(1)(2) // 3
- 解释:上面我们可以看到第一个普通的相加函数很容易理解,但是第二个函数是柯里化以后即将普通函数的
2个参数先用一个函数接收了第一个参数,再返回一个函数去处理第二个参数
,意思差不多就是封装了一层 - 与bind()函数:(在上一个博客高级技巧一中已经介绍了函数绑定和bind函数):
函数柯里化就被作为函数绑定的一部分去构造更复杂的bind()函数
- 举例:
function bind(fn, context){
var args = Array.prototype.slice.call(arguments, 2);
return function(){
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return fn.apply(context, finalArgs);
};
}
- 自己通用的封装方法:
// 初步封装
var currying = function(fn) {
// args 获取第一个方法内的全部参数
var args = Array.prototype.slice.call(arguments, 1)
return function() {
// 将后面方法里的全部参数和args进行合并
var newArgs = args.concat(Array.prototype.slice.call(arguments))
// 把合并后的参数通过apply作为fn的参数并执行
return fn.apply(this, newArgs)
}
}
-
复习:slice()方法
-
例子的解析:
- 1、参数解释:curry() 的
第一个参数
是要进行柯里化的函数,其他参数
是要传入的值 - 2、获取:获取第一个参数之后的所有参数:对arguments对象运用slice()方法,并
传入参数 1 表
示被返回的数组包含从第二个参数开始的所有参数 - 3、存放所有参数:
newArgs数组将所有内部函数和外部函数的参数数组组合起来
- 4、传递结果:用
apply
函数,此处考虑到了执行环境,用的this
- 1、参数解释:curry() 的
-
函数柯里化的好处:
-
参数复用(正则表达式验证举例:
-
构造bind函数
- 复习:1、arguments
-
// arguments 是一个对应于传递给函数的参数的类数组对象
function a(){
console.log(arguments);
}
a(); // Arguments [callee: ƒ, Symbol(Symbol.iterator): ƒ]
a(1,2,3); // Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
-
2、 ==bind()==方法主要就是将函数绑定到某个对象,bind()会创建一个函数,函数体内的this对象的值会被绑定到传入bind()中的第一个参数的值
-
参数复用举例:
// 正常正则验证字符串 reg.test(txt)
// 函数封装后
function check(reg, txt) {
return reg.test(txt)
}
check(/\d+/g, 'test') //false
check(/[a-z]+/g, 'test') //true
// Currying后
function curryingCheck(reg) {
return function(txt) {
return reg.test(txt)
}
}
var hasNumber = curryingCheck(/\d+/g)
var hasLetter = curryingCheck(/[a-z]+/g)
hasNumber('test1') // true
hasNumber('testtest') // false
hasLetter('21212') // false
-
解析:这个正则校验只要用check函数就行了,但是如果要校验较多的话,需要将第一个参数进行复用,这样别的地方就能够直接调用hasNumber,hasLetter等函数,让参数能够复用
-
性能:
- 1、
存取arguments对象
通常要比存取命名参数要慢一点 - 2、一些老版本的浏览器在
arguments.length
的实现上是相当慢的 - 3、使用fn.apply( … ) 和 fn.call( … )
通常比直接调用fn( … ) 稍微慢点
- 4、创建大量嵌套作用域和闭包函数会带来花销,
无论是在内存还是速度上
(一直有提到过这个点)
- 1、
——参考于https://www.jianshu.com/p/2975c25e4d71
摘录:JavaScript 中的
柯里化函数和绑定函数
提供了强大的动态函数创建功能
。使用 ==bind() 还是 curry()==要根据是否需要 object 对象响应来决定。它们都能用于创建复杂的算法和功能,当然两者都不应滥用,因为每个函数都会带来额外的开销
——《JavaScript高级程序设计》
- 扩展题目:
// 实现一个add方法,使计算结果能够满足如下预期:
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;
function add() {
// 第一次执行时,定义一个数组专门用来存储所有的参数
var _args = Array.prototype.slice.call(arguments);
// 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
var _adder = function() {
_args.push(...arguments);
return _adder;
};
// 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
_adder.toString = function () {
return _args.reduce(function (a, b) {
return a + b;
});
}
return _adder;
}
add(1)(2)(3) // 6
add(1, 2, 3)(4) // 10
add(1)(2)(3)(4)(5) // 15
add(2, 6)(1) // 9