灏天阁

参与者模式

· Yin灏
/**
 * 参与者模式
 * 在特定的作用域中执行给定的函数,并将参数原封不动的传递。
 */

/**
 * 函数绑定
 */
function bind(fn, context) {
  // 闭包返回新函数
  return function () {
    return fn.apply(context, arguments);
  };
}
// 测试用例
var demoObj = {
  title: "这是一个例子",
};
function demoFn() {
  console.log(this.title);
}
var bindFn = bind(demoFn, demoObj);
demoFn(); // undefined
bindFn(); // 这是一个例子

/**
 * 应用于事件
 */
var btn = document.getElementsByTagName("button")[0];
var p = document.getElementsByTagName("p")[0];
function demoFn() {
  console.log(arguments, this);
}

// 未设置提供参与对象
var bindFn = bind(demoFn);
btn.addEventListener("click", bindFn); // window

// 提供 btn 元素参与对象
var bindFn = bind(demoFn, btn); // <button></buton>

// 提供 p 元素参与对象
var bindFn = bind(demoFn, p); // <p>hello</p>

// 移除事件
btn.removeEventListener("click", bindFn);

/**
 * 原生 bind 方法
 */
var bindFn = demoFn.bind(p);

/**
 * 函数柯里化
 * 函数柯里化的思想时对函数的参数分割,这有点像其他面向语言中的类的多态,就是根据传参不同,可以让一个函数存在多种状态,只不过函数柯里化处理的都是函数,
 * 通过函数柯里化器对 add 方法实现的多态拓展且不需要像以前那样明确声明函数,因为函数的创建过程已经在函数柯里化器中完成了
 */
// 函数柯里化
function curry(fn) {
  // 缓存数组 slice 方法
  var Slice = [].slice;
  // 从第二个参数开始截取参数
  var args = Slice.call(arguments, 1);
  // 闭包返回新函数
  return function () {
    // 将参数(类数组)转化为数组
    var addArgs = Slice.call(arguments),
      // 拼接参数
      allArgs = args.concat(addArgs);
    // 返回新函数
    return fn.apply(null, allArgs);
  };
}

// 创建一个加法器,然后对加法器进行拓展
// 加法器
function add(num1, num2) {
  return num1 + num2;
}
// 加 5 加法器
function add5(num) {
  return add(5, num);
}
// 测试 add 加法器
console.log(add(1, 2)); // 3
// 测试加 5 加法器
console.log(add5(6)); // 11
// 函数柯里化创建加 5 加法器
var add5 = curry(add, 5);
console.log(add5(7)); // 12

var add7and8 = curry(add, 7, 8);
console.log(add7and8()); // 15

/**
 * 重构 bind
 */
function bind(fn, context) {
  var Slice = Array.prototype.slice,
    args = Slice.call(arguments, 2);
  return function () {
    var addArgs = Slice.call(arguments),
      allArgs = addArgs.concat(args);
    return fn.apply(context, allArgs);
  };
}

var demoData1 = {
    text: "这是第一组数据",
  },
  demoData2 = {
    text: "这是第二组数据",
  };

var bindFn = bind(demoFn, btn, demoData1); // [MouseEvent, Object] <button>按钮</button>

var bindFn = bind(demoFn, btn, demoData1, demoData2); // [MouseEvent, Object, Object] <button>按钮</button>

var bindFn = bind(demoFn, p, demoData1); // [MouseEvent, Object] <p>hello</p>

/**
 * 兼容版本
 */
// 兼容各个浏览器
if (Function.prototype.bind === undefined) {
  Function.prototype.bind = function (context) {
    var Slice = Array.prototype.slice,
      args = Slice.call(arguments, 1),
      that = this;
    return function () {
      var addArgs = Slice.call(arguments),
        allArgs = args.concat(addArgs);
      return that.apply(context, allArgs);
    };
  };
}

/**
 * 反柯里化
 * 目的是方便我们对方法的调用
 */
// 反柯里化
Function.prototype.uncurry = function () {
  // 保存当前对象
  var that = this;
  return function () {
    return Function.prototype.call.apply(that, arguments);
  };
};
// 用 Object.prototype.toString 校验对象类型时:
// 获取校验方法
var toString = Object.prototype.toString.uncurry();
// 测试对象数据类型
console.log(toString(function () {})); // [object Function]
console.log(toString([])); // [object Array]

// 用数组 push 的方法为对象添加数据成员
// 保存数组 push 方法
var push = [].push.uncurry();
// 创建一个对象
var demoArr = {};
// 通过 push 方法为对象添加数据成员
push(demoArr, "第一个成员", "第二个成员");
console.log(demoArr); // Object { 0: '第一个元素', 1: '第二个成员', length: 2 }

/**
 * 函数柯里化的好处
 * 在函数式编程中,我们往往希望一个函数处理的问题竟可能的单一,而不是讲一大堆的过程交给一个函数来处理。要符合设计模式中的单一职责原则。
 * 我们可以将每次传入的参数在单一的函数中进行处理,处理完后在下一个函数中再使用处理后的结果。
 */

//代码1
function sum1(x, y, z) {
  return x + y + z;
}

function sumKe(x) {
  return function (y) {
    return function (z) {
      return x + y + z;
    };
  };
}

let result = sumKe(10)(20)(30);
console.log(result);

/**
 * 柯里化函数的简化
 */
let sum = (x) => (y) => (z) => {
  return x + y + z;
};

- Book Lists -