JavaScript 系列 - 偏函数、柯里化、反柯里

76 阅读2分钟

偏函数

概念

固定一个函数的一些参数,然后产生另一个更小元的函数

函数参数的个数

应用

function partial(fn) {
  var args = [].slice.call(arguments, 1);
  return function () {
    var newArgs = args.concat([].slice.call(arguments));
    return fn.apply(this, newArgs);
  };
}
function add(a, b) {
  return a + b;
}
var addOne = partial(add,1)
console.log(addOne(2));
// lodash.partial 占位符
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 currying(func) {
    const args = [];
    return function result(...rest) {
        if (rest.length === 0) {
          return func(...args);
        } else {
          args.push(...rest);
        	return result;
        }
    }
}
const add = (...args) => args.reduce((a, b) => a + b);
const sum = currying(add);
sum(1,2)(3); // 未真正求值
sum();
let obj = {
  name: "a",
};
const fun = function () {
  console.log(this.name);
}.bind(obj);

fun();

动态创建函数

const addEvent = (function () {
  if (window.addEventListener) {
    return function (type, el, fn, capture) {
      el.addEventListener(type, fn, capture);
    };
  } else if (window.attachEvent) {
    return function (type, el, fn) {
      el.attachEvent("on" + type, fn);
    };
  }
})();
// 惰性函数
function addEvent(type, el, fn, capture = false) {
  // 重写函数
  if (window.addEventListener) {
    addEvent = function (type, el, fn, capture) {
      el.addEventListener(type, fn, capture);
    };
  } else if (window.attachEvent) {
    addEvent = function (type, el, fn) {
      el.attachEvent("on" + type, fn);
    };
  }
  // 执行函数,有循环爆栈风险
  addEvent(type, el, fn, capture);
}

参数复用

const toStr = Function.prototype.call.bind(Object.prototype.toString);

// 改造前
[1, 2, 3].toString(); // "1,2,3"
'123'.toString(); // "123"
123.toString(); // SyntaxError: Invalid or unexpected token
Object(123).toString(); // "123"

// 改造后
toStr([1, 2, 3]); 	// "[object Array]"
toStr('123'); 		// "[object String]"
toStr(123); 		// "[object Number]"
toStr(Object(123)); // "[object Number]"

反柯里

概念

扩大使用范围,创建一个应用范围更广的函数,使本来只有特定对象才适用的方法,扩展到更多的对象。

应用

Function.prototype.unCurrying = function () {
  // this 是传递过来的函数
  var self = this;
  return function () {
    // arguments 是调用 unCurrying 函数以后传递的参数
    // 第一个参数是 call 函数的 this
    return Function.prototype.call.apply(self, arguments);
  };
};
function unCurrying(fn) {
  return function () {
    var args = [].slice.call(arguments);
    var that = args.shift();
    return fn.apply(that, args);
  };
}

// 轻提示
function Toast(option) {
  this.prompt = "";
}
Toast.prototype.show = function (a) {
  console.log(this.prompt);
};

// 新对象
var obj = {
  prompt: "新对象",
};

// 使用
var objShow = Toast.prototype.show.unCurrying();
// var objShow = unCurrying(Toast.prototype.show);
objShow(obj);

借用原生方法

  • 对象借用数组方法
Function.prototype.unCurrying = function () {
  var self = this;
  return function () {
    return Function.prototype.call.apply(self, arguments);
  };
};
var obj = {};
var push = Array.prototype.push.unCurrying();
push(obj, "a");
console.log(Array.from(obj));
  • arguments 转数组
var slice = Array.prototype.slice.unCurrying()
~function a() {
    console.log(slice(arguments,0));
}(1,2,3)
  • getElementById
var call = Function.prototype.call.unCurrying();
var fn = function (id) {
  console.log(this.getElementById(id));
};
call(fn, document, "a");
  • 批量增强对象
var add_fn = function (obj, fn_keys) {
  for (var i = 0, ary = fn_keys.split(","), fn; (fn = ary[i++]); ) {
    ~(function (fn) {
      var new_fn = Array.prototype[fn].unCurrying();
      obj[fn] = function () {
        console.log([this].concat(Array.prototype.slice.call(arguments)));
        new_fn.apply(
          this,
          [this].concat(Array.prototype.slice.call(arguments))
        );
        return this;
      };
    })(fn);
  }
};
var A = function () {};
add_fn(A.prototype, "push,indexOf,shift,pop,forEach");
var a = new A();
a.push(1)
  .push(2)
  .push(3)
  .forEach(function (n) {
    console.log(n);
  });
console.log(a);