JavaScript函数重载模拟

我们从结果向实现推,首先看我们要实现什么样的效果:

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];
        });
    }
});