JavaScript 函数高阶用法
函数柯里化
用途
将函数的参数通过连续调用分开来传
例子
function add(a, b) {
return a + b;
}
// 执行 add 函数,一次传入两个参数即可
add(1, 2); // 3
// 假设有一个 curry 函数可以做到柯里化
var addCurry = curry(add);
addCurry(1)(2); // 3
实现
function curry(fn, args, holes) {
length = fn.length;
args = args || [];
holes = holes || [];
return function() {
var _args = args.slice(0),
_holes = holes.slice(0),
argsLen = args.length,
holesLen = holes.length,
arg,
i,
index = 0;
for (i = 0; i < arguments.length; i++) {
arg = arguments[i];
// 处理类似 fn(1, _, _, 4)(_, 3) 这种情况,index 需要指向 holes 正确的下标
if (arg === _ && holesLen) {
index++;
if (index > holesLen) {
_args.push(arg);
_holes.push(argsLen - 1 + index - holesLen);
}
}
// 处理类似 fn(1)(_) 这种情况
else if (arg === _) {
_args.push(arg);
_holes.push(argsLen + i);
}
// 处理类似 fn(_, 2)(1) 这种情况
else if (holesLen) {
// fn(_, 2)(_, 3)
if (index >= holesLen) {
_args.push(arg);
}
// fn(_, 2)(1) 用参数 1 替换占位符
else {
_args.splice(_holes[index], 1, arg);
_holes.splice(index, 1);
}
} else {
_args.push(arg);
}
}
if (_holes.length || _args.length < length) {
return curry.call(this, fn, _args, _holes);
} else {
return fn.apply(this, _args);
}
};
}
var _ = {};
var fn = curry(function(a, b, c, d, e) {
console.log([a, b, c, d, e]);
});
// 验证 输出全部都是 [1, 2, 3, 4, 5]
fn(1, 2, 3, 4, 5);
fn(_, 2, 3, 4, 5)(1);
fn(1, _, 3, 4, 5)(2);
fn(1, _, 3)(_, 4)(2)(5);
fn(1, _, _, 4)(_, 3)(2)(5);
fn(_, 2)(_, _, 4)(1)(3)(5);
偏函数
用途
在多次调用某个函数时,将函数的公共参数进行封装,返回一个新的函数
例子
function add(a, b) {
return a + b;
}
// 执行 add 函数,一次传入两个参数即可
add(1, 2); // 3
// 假设有一个 partial 函数可以做到局部应用
var addOne = partial(add, 1);
addOne(2); // 3
addOne(3); // 4
实现
var _ = {};
function partial(fn) {
var args = [].slice.call(arguments, 1);
return function() {
var position = 0,
len = args.length;
for (var i = 0; i < len; i++) {
args[i] = args[i] === _ ? arguments[position++] : args[i];
}
while (position < arguments.length) args.push(arguments[position++]);
return fn.apply(this, args);
};
}
惰性函数
用途
在多次调用某个函数时,只返回第一次调用的结果,类似缓存,原理是重写函数
实现
function addEvent(type, el, fn) {
if (window.addEventListener) {
addEvent = function(type, el, fn) {
el.addEventListener(type, fn, false);
};
} else if (window.attachEvent) {
addEvent = function(type, el, fn) {
el.attachEvent("on" + type, fn);
};
}
}
函数组合
用途
在需要对某一个值进行多个函数的过滤时,可以使用函数组合
例子
var toUpperCase = function(x) {
return x.toUpperCase();
};
var hello = function(x) {
return "HELLO, " + x;
};
var greet = function(x) {
return hello(toUpperCase(x));
};
greet("kevin");
实现
// 需求:输入 'kevin daisy kelly',返回 'K.D.K'
// 非 pointfree,因为提到了数据:name
var initials = function(name) {
return name
.split(" ")
.map(compose(toUpperCase, head))
.join(". ");
};
// pointfree
// 先定义基本运算
var split = curry(function(separator, str) {
return str.split(separator);
});
var head = function(str) {
return str.slice(0, 1);
};
var toUpperCase = function(str) {
return str.toUpperCase();
};
var join = curry(function(separator, arr) {
return arr.join(separator);
});
var map = curry(function(fn, arr) {
return arr.map(fn);
});
var initials = compose(join("."), map(compose(toUpperCase, head)), split(" "));
initials("kevin daisy kelly");
函数记忆
用途
某个函数在多次调用时,如果参数没变,直接拿缓存下来的数据
例子
function add(a, b) {
return a + b;
}
// 假设 memoize 可以实现函数记忆
var memoizedAdd = memoize(add);
memoizedAdd(1, 2); // 3
memoizedAdd(1, 2); // 相同的参数,第二次调用时,从缓存中取出数据,而非重新计算一次
实现
var memoize = function(func, hasher) {
var memoize = function(key) {
var cache = memoize.cache;
var address = "" + (hasher ? hasher.apply(this, arguments) : key);
if (!cache[address]) {
cache[address] = func.apply(this, arguments);
}
return cache[address];
};
memoize.cache = {};
return memoize;
};