参与者模式
·
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;
};