林秀栋的技术博客

设计模式(十七) 参与者模式

从十七开始的设计模式属于技巧型设计模式

//在特定作用域中执行给定函数,并将参数原封不动传递。
//比如事件绑定函数,无法直接传入参数,可在回调函数中引用新函数,并传参。
A.on = function(){
	if(dom.addEventListener){
		dom.addEventListener(type, function(e){
			fn.call(dom, e, data);	//在dom环境中调用fn
		})
	}else{
	}
}
//但这样添加的事件就不能移除回调函数了。且回调函数是匿名函数,所以需要借助参与者模式,即让更多对象参与执行时的函数。
function bind(fn, context){	//bind是浏览器内置方法,但低版本浏览器不支持
	return function(){	//闭包返回新函数
		return fn.apply(context, arguments);
	}
}
var dO = {
	title: 'demo'
}
function dF(){	//测试方法
	console.log(this.title);
}
var bF = bind(dF, dO);
dF();	//undefined
bF();	//demo
//直接使用dF无效,用了bind才能取得dO中的title,作为一个参与者,作用环境在其他地方

//应用于事件
function demoFn(){
	console.log(arguments, this);
}
var bindFn = bind(demoFn);
btn.addEventListener('click', bindFn);	//[MouseEvent] Window
var bindFn = bind(demoFn, btn);	//[MouseEvent] <button>…</button>
btn.removeEventListener('click', bindFn);
//this返回的即运行的作用域,而且这种方法添加的事件还可以通过removeEventListener移除。
//在高版本浏览器中原生的bind方法也可:var bindFn = demoFn.bind(btn);
//但这只是第一步,第二步要给函数添加额外的自定义数据参数,借助函数的柯里化。

//函数的柯里化的思想是对函数的参数分割,根据参数不同,让函数存在多种状态。以函数为基础,借助柯里化器伪造其他函数,让伪造函数在执行时调用这个基函数完成不同功能。
//函数柯里化器
function curry(fn){
	var Slice = [].slice;	//缓存数组slice方法Array.prototype.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;
}
function add5(num){	//加5
	return add(5, num);
}
var add5 = curry(add, 5);
add5(7);	//12
var add7and8 = curry(add, 7, 8);
add7and8();	//15
//重构bind
function bind(fn, context){
	var Slice = Array.prototype.slice;	//缓存数组slice方法
	var args = Slice.call(arguments, 2);
	return function(){
		var addArgs = Slice.call(arguments),
			allArgs = addArgs.concat(args);
		return fn.apply(context, allArgs);
	}
}
var bindFn = bind(demoFn, btn, data1, data2);
var bindFn = bind(demoFn, p, data1);
//浏览器内置bind方法的使用:
var bindFn = demoFn.bind(p, data1);
//兼容浏览器写法
if(Function.prototype.bind === undefined){
	Function.prototype.bind = function(context){
		var Slice = Array.prototype.slice;	//缓存数组slice方法
		var args = Slice.call(arguments, 1);
		var 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);
	}
}
//用数组的push方法为对象添加成员
var push = [].push.uncurry();
var demoArr = {};
push(demoArr, ‘demo1’, ‘demo2’);
demoArr //{0:”demo1”, 1:”demo2”, length:2}