熟悉一下常见的高阶函数
beforeSay
// 之前实现的业务代码,现在需要扩展当前的业务
function say(a, b) {
console.log('say', a, b);
}
// 给 say 方法,添加一个方法,在 say 执行之前调用
Function.prototype.before = function (callback) {
return (...args) => {
callback();
this(...args);
};
}
let beforeSay = say.before(function () {
// 此处是扩展的业务代码
console.log('before say');
})
beforeSay('hello', 'world');
单例设计模式
// 利用闭包的保护机制,避免变量全局污染
let weatherModule = (function () {
let _default = 'beijing';
let queryWeather = function () { };
let setWeather = function () { };
return {
// ES6 setWeather: setWeather
setWeather
}
})();
惰性函数
-
普通事件绑定
/** * 兼容性 事件绑定 * @param {*} ele dom 节点 * @param {*} type 事件类型 * @param {*} func 触发函数 */ function emit(ele, type, func) { if (ele.addEventListener) { // dom2 事件 IE8 以上 ele.addEventListener(type, func); } else if (ele.attachEvent) { // dom2 事件 IE8 以下 ele.attachEvent(type, func); } else { // dom0 事件 ele['on' + type] = func; } } //调用 2 次,需要重复判断 emit(box, 'click', fn1); emit(box, 'click', fn2);
-
惰性思想实现
function emit(ele, type, func) { if (ele.addEventListener) { // 判断是该事件类型后,直接重写 emit函数 emit = function (ele, type, func) { ele.addEventListener(type, func, false); } } else if (ele.attachEvent) { emit = function (ele, type, func) { ele.attachEvent(type, func); } } else { emit = function (ele, type, func) { ele['on' + type] = func; } } // 调用emit,完成事件绑定 emit(ele, type, func); } //调用 2 次,第二次不会再重复判断,惰性思想,优化了性能 emit(box, 'click', fn1); emit(box, 'click', fn2);
手写 bind
// ES5 实现
~function (proto) {
function bind(context) {
context = context || window;
var outerArgs = Array.prototype.slice.call(arguments, 1);
// this: 要处理的函数
var _this = this;
return function proxy() {
var innerArgs = Array.prototype.slice.call(arguments, 0);
_this.apply(context, outerArgs.concat(innerArgs));
}
}
proto.bind = bind;
}(Function.prototype);
// ES6 实现
~function (proto) {
function bind(context = window, ...outerArgs) {
// this: 要处理的函数
let _this = this;
return function proxy(...innerArgs) {
_this.call(context, ...outerArgs.concat(innerArgs));
}
}
proto.bind = bind;
}(Function.prototype);
柯理化(Currying)函数
柯里化是编程语言中的一个通用的概念,是指把接收多个参数的函数变换成接收单一参数的函数,嵌套返回直到所有参数都被使用并返回最终结果,柯里化不会调用函数,它只是对函数进行转换;
应用及特点:
- 参数复用:当在多次调用同一个函数,并且传递的参数绝大多数是相同的,那么就可以用柯里化进行函数的复用;
- 提前返回:多次调用内部判断,可以直接把第一次判断的结果返回外部接收;
- 延迟执行:避免重复的去执行程序,等真正需要结果的时候再执行;
-
实现效果:
add(1); // 1 add(1)(2); // 3 add(1)(2)(3); // 6 add(1)(2, 3); // 6 add(1, 2)(3); // 6 add(1, 2, 3); // 6
-
实现代码:
JavaScriptJavaScript/* * 实现方案一 */ function add(...outerArgs) { // 重写了 ADD,第二次之后将所有参数全部放到数组中 add = function (...innerArgs) { outerArgs.push(...innerArgs); return add; }; add.toString = function () { return outerArgs.reduce((x, y) => x + y); }; return add; } let res = add(1, 2)(3)(4)(5)(6, 7); alert(res); //=>alert会把输出的值转换为字符串(toString()) /* * 第一次执行 ADD outerArgs=[1,2] 重写了ADD * 第二次执行 ADD innerArgs=[3] outerArgs=[1,2,3] * 第三次执行 ADD innerArgs=[4] outerArgs=[1,2,3,4] * ...... * outerArgs=[1,2,3,4,5,6,7] */ // console.log(res.toString());
/* * 实现方案二 :currying(柯理化函数封装+递归) */ function currying(anonymous, length) { // 直接返回 add 函数 return function add(...args) { // 递归终止条件 if (args.length == length) { return anonymous(...args); } return currying(anonymous.bind(null, ...args), length - args.length); } } let add = currying(function anonymous(...args) { return args.reduce((x, y) => x + y); }, 4); /* * AO(currying) * anonymous=求和函数 * length=4 * * ADD第一次执行 args=[1,2] * currying第二次执行 * anonymous=求和函数 预先传递的参数[1,2] * length=2 * ADD第二次执行 args=[3] * currying第三次执行 * anonymous=求和函数 预先传递的参数[3] * length=1 * ADD函数第三次执行 args=[4] * 把上一次的求和函数执行(4) * 弊端:一层一层形成多次闭包 */ console.log(add(1, 2)(3)(4));
管道(pipe)函数
管道(pipe)函数也叫函数扁平化:
- 后一个函数作为前一个函数的参数;
- 最后一个函数可以接受多个参数,前面的函数只能接受单个参数;
- 后一个的返回值传给前一个;
前置知识
-
reduce 的语法
/** * callback 回调函数 * - accumulator 累计器 * - currentValue 当前值 * - index 当前索引 * - array 调用 reduce 的数组 * initialValue 作为第一次调用 callback 函数时的第一个参数的值,如果没有提供初始值,则将使用数组中的第一个元素 */ arr.reduce(callback(accumulator, currentValue, index, array), initialValue)
-
使用 reduce 的第一个参数,每次调用的参数和返回值如下表:
[0, 1, 2, 3, 4].reduce(function (accumulator, currentValue, currentIndex, array) { return accumulator + currentValue; });
callback accumulator currVal Index array return value 第一次调用 0 1 1 [0, 1, 2, 3, 4] 1 第二次调用 1 2 2 [0, 1, 2, 3, 4] 3 第三次调用 3 3 3 [0, 1, 2, 3, 4] 6 第四次调用 6 4 4 [0, 1, 2, 3, 4] 10 -
提供一个初始值作为 reduce 方法的第二个参数,每次调用的参数和返回值如下表:
[0, 1, 2, 3, 4].reduce((accumulator, currentValue, currentIndex, array) => { return accumulator + currentValue }, 10)
callback accumulator currVal Index array return value 第一次调用 10 0 0 [0, 1, 2, 3, 4] 10 第二次调用 10 1 1 [0, 1, 2, 3, 4] 11 第三次调用 11 2 2 [0, 1, 2, 3, 4] 13 第四次调用 13 3 3 [0, 1, 2, 3, 4] 16 第五次调用 16 4 4 [0, 1, 2, 3, 4] 20
代码实现
-
未实现扁平化
let fn1 = function (x) { return x + 10; } let fn2 = function (x) { return x * 10; } let fn3 = function (x) { return x / 10; } // 输出16 console.log(fn3(fn1(fn2(fn1(5)))));
-
实现函数扁平化
JavaScriptJavaScript// 使用 reduce 实现扁平化(实现的是,参数从前到后计算) /** * 手写compose * @param {*} funcs 函数集合 * @return {*} 返回一个代理函数 */ function compose(...funcs) { return function proxy(...args) { // 第一次调用传递的参数集合 let len = funcs.length; if (len === 0) { // 一个函数都不传,不需要执行,返回args // compose()(5) :args 为 5 return args; } if (len === 1) { return funcs[0](...args); } // 两个及以上的函数 return funcs.reduce((fun, y) => { console.log(fun, y) // [Function: fn1] [Function: fn2] // 150 [Function: fn1] // 160 [Function: fn3] // 16 // 第一次执行的时候 fun 是函数,之后都是执行函数出来的结果赋值给 fun return typeof fun === 'function' ? y(fun(...args)) : y(fun); }); } } console.log(compose()(5));// =>5 console.log(compose(fn1)(5));// =>5+10=15 console.log(compose(fn1, fn2)(5));// =>5+10=15 fn2(15)=150... console.log(compose(fn1, fn2, fn1, fn3)(5));// =>16
// redux 使用 reduce 实现函数扁平化(实现的是,参数从后到前计算) function compose(...funcs) { //没有传入函数参数,就返回一个默认函数(直接返回参数) if (funcs.length === 0) { // compose()(5) :args 为 5 return arg => arg // return function (arg) { // return arg; // } } if (funcs.length === 1) { // 单元素 数组时调用 reduce,会直接返回该元素,不会执行 callback,所以这里手动执行 // compose(fn1)(5) return funcs[0] } // 依次拼凑执行函数 // reduce不传第二个参数:a初始值是数组第一项的值,然后每一次返回的结果作为下一次的值 return funcs.reduce(function (a, b) { console.log(a, b); // [Function: fn1] [Function: fn2] // fn1(fn2(...args)) [Function: fn1] // fn1(fn2(fn1(...args))) [Function: fn3] // fn1(fn2(fn1(fn3(...args)))) return function (...args) { return a(b(...args)) } }) // 等价于: // return funcs.reduce((a, b) => (...args) => a(b(...args))) } // fn1(fn2(fn1(fn3(...args)))) console.log(compose(fn1, fn2, fn1, fn3)(5));// =>115
剑指 Offer 30.包含min函数的栈
上一篇