我们从结果向实现推,首先看我们要实现什么样的效果:
css(hi,"color","red")
css([hi,hello],"color","red")
css(hi,{"border":"1px solid #000","width":"200px"})
var color = css(hi,"color")
Overload("css", window, {
"object,string,string": function(el,key,val){
el.style[key] = val;
},
"array,string,string": function(els,key,val){
for(var i=0;i<els.length;i++){
els[i].style[key] = val;
}
},
"object,object": function(el,kv){
for(var i in kv){
el.style[i] = kv[i];
}
},
"object,string": function(el,key){
return el.style[key];
}
});
/*
JavaScript函数重载模拟
name : 函数名
bind : 函数需要绑定到的对象
fn_objs : 键值对函数对象,键位以逗号隔开的类型(number,string,object,undefined,boolean,array,*)字符串,其中*为万能类型,值为对应的函数,
如:{"string,string":function(x,y){},"string,number":functioin(x,y){}}
*/
var Overload = function(name,bind,fn_objs){
var dict_name = "_"+name+"_dict",dict;
dict = bind[dict_name] = bind[dict_name] || {};
for(var i in fn_objs){
dict[i] = fn_objs[i];
}
var is_match = function(x,y){
if(x==y) return true;
if(x.indexOf("*")==-1) return false;
var x_arr = x.split(","),y_arr = y.split(",");
if(x_arr.length != y_arr.length) return false;
while(x_arr.length){
var x_first = x_arr.shift(),y_first = y_arr.shift();
if(x_first!="*" && x_first!=y_first) return false;
}
return true;
};
bind[name] = function(){
var args = arguments,args_len = args.length,args_types=[],args_type,match_fn = function(){};
for(var i=0;i<args_len;i++){
var type = typeof args[i];
type=="object" && (args[i] instanceof Array) && (type="array");
args_types.push(type);
}
args_type = args_types.join(",");
for(var k in dict){
if(is_match(k,args_type)){
match_fn = dict[k];
break;
}
}
return match_fn.apply(this,args);
};
};
因为采用 typeof 来动态监测参数类型,而 typeof 又只能检测到的值只有 number、string、object、undefined、boolean,所以类型的制定只能从这些值中设定,实现中还加入了万能类型“*”,由于 array 属于常用类型,所以又特别添加了对 array 类型的支持。
基本原理是:将以类型串为键,函数为值的字典挂到需要绑定的对象上,命名为"_"+fn_name+"_dict",可用bind["_"+fn_name_"_dict"]来访问此对象,接着生成一个函数,在函数里靠判断arguments生成的类型串来从上述字典中匹配到相应的函数。
上面的写法 ,始终不好看,有如下改进:
Overload = function(fn_objs){
var is_match = function(x,y){
if(x==y) return true;
if(x.indexOf("*")==-1) return false;
var x_arr = x.split(","),y_arr = y.split(",");
if(x_arr.length != y_arr.length) return false;
while(x_arr.length){
var x_first = x_arr.shift(),y_first = y_arr.shift();
if(x_first!="*" && x_first!=y_first) return false;
}
return true;
};
var ret = function(){
var args = arguments,
args_len = args.length,
args_types=[],
args_type,
fn_objs = args.callee._fn_objs,
match_fn = function(){};
for(var i=0;i<args_len;i++){
var type = typeof args[i];
type=="object" && (args[i].length>-1) && (type="array");
args_types.push(type);
}
args_type = args_types.join(",");
for(var k in fn_objs){
if(is_match(k,args_type)){
match_fn = fn_objs[k];
break;
}
}
return match_fn.apply(this,args);
};
ret._fn_objs = fn_objs;
return ret;
};
String.prototype.format = Overload({
"array": function(params){
var reg = /{(\d+)}/gm;
return this.replace(reg,function(match,name){
return params[~~name];
});
},
"object": function(param){
var reg = /{([^{}]+)}/gm;
return this.replace(reg,function(match,name){
return param[name];
});
}
});