js实现重载
- 使用javascript(es6)来模拟重载(其实也没用多少es6,就是一个let)
- 这个实现较之一般实现来说,模拟的更像重载,可以根据参数类型和个数来选择相应的函数
- 核心是eval以及一个对象数组
- 转载请申明出处
首先的首先
先看看一般重载函数怎么实现的吧,网上一大堆,自己某度就能了解了
首先先上代码(不想看我的思路的人直接拿走用吧)
function reloadGenerator(){
this.actFns=[];
let that = this;
let actObj = function(){
let inArgs=arguments;
let typeForFn =that.actFns;
let l = inArgs.length-1;
let strArg = "";
let fn = typeForFn[l];
for(let i = 0; i<inArgs.length;i++){
strArg +=",inArgs["+i+"]";
if(fn[queryType(inArgs[i])]){
fn = fn[queryType(inArgs[i])]
}else{
console.log('函数不存在: 要调用的函数不存在');
}
}
strArg = strArg.substr(1);//获取参数字符串
let invoca = "fn("+strArg+")";
eval(invoca);
//-------***********
function queryType(arg){
if(arg.constructor == String ){
return "string";
}
if(arg.constructor == Number){
return "number";
}
if(arg.constructor == Boolean){
return "boolean";
}
if(arg.constructor == Array){
return "array";
}
if(arg.constructor == Object){
return "object";
}
if(arg.constructor == Function){
return "function";
}
return "undefined";
}
}
this.addReload=function(arg,fn){
let l = fn.length-1;
if(!this.actFns[l]){
this.actFns[l]={}
}
let addObj = this.actFns[l];
for(let i = 0; i<arg.length; i++){
if(i == arg.length-1){
//by wyx0k 手动水印haha
addObj[arg[i]]=fn;
}else{
if(!addObj[arg[i]]){
addObj[arg[i]]={};
}
}
addObj=addObj[arg[i]];
}
}
this.init = function(){
return actObj;
}
this.remove = function(arr){
let l = arr.length-1;
let removeObj = this.actFns[l]
for(let i = 0; i<arr.length; i++){
if(removeObj[arr[i]]){
if(i == l){
removeObj[arr[i]] = null;
}
removeObj = removeObj[arr[i]]
}else{
console.log("函数不存在:无法删除不存在的重载函数");
return;
}
}
}
if(arguments.length==0){
return this;
}
let args = [];
let fns = [];
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i][0]);
fns.push(arguments[i][1]);
}
for (let i = 0; i < fns.length; i++) {
let l = fns[i].length-1;
if(!this.actFns[l]){
this.actFns[l]={}
}
let genObj = this.actFns[l];
for (let k = 0; k < args[i].length; k++) {
if(k==args[i].length-1){
//by wyx0k 手动水印haha
genObj[args[i][k]]=fns[i];
}else{
if(!genObj[args[i][k]]){
genObj[args[i][k]]={};
}
}
genObj = genObj[args[i][k]];
}
}
return actObj;
}
调用方式
调用可以使用两种方式,一次生成调用(不知道算不算静态调用)和动态调用
一次生成
这种调用的方式,一旦生成了重载函数,就无法改变了.但是一般也就这么用,没事的.
对方不和你说话并向你抛了一段代码
//创建
let test = new reloadGenerator(
[
['boolean','function'],
function(tr,fn){
fn(tr)
}
],
[
['boolean','number'],
function(tr,num){
console.log(num+"----------"+tr)
}
],
[
['array','object'],
function(arr,obj){
console.log(obj);
console.log(arr.join());
}
]
);
//使用
test(true,function(t){
alert(!t);
});
test(true,666);
test([1,2,3],{ok:true,num:123,arr:[{as:123456879}]});
主要呢,就是传一个n个数组进去,一个数组代表定义了一个重载函数,然后呢,每个数组里第一个是重载函数的参数类型(注意这个就相当于定义了参数的顺序\个数\类型),第二个是重载函数的函数体,第二个重载函数的形参要不要写?当然要写了,方便记忆,以及函数里面调用啊.
然后用一个变量接收,这个变量就是生成好了的重载函数了~
动态调用
**如果我的重载函数是不固定的,我一会想多加一个重载函数一会又不想要了(别问我为什么这么不专一),那我咋办?
不要慌,看代码
let test2 = new reloadGenerator();//如果想要动态的使用,那么就不要在这传参
test2.addReload(['string','number'],function(name,score){
alert(name+"----------"+score);
});
test2.addReload(['string','number','boolean'],function(name,score,sex){
alert(name+"----------"+score+"----------"+sex);
});
let fn = test2.init();//每次新增了重载函数都要记得init一下哟
fn('okman',12,true);
fn('haska',16);
test2.remove(['string','number','boolean1']);
fn('okman',12,true);
test2.remove(['string','number','boolean']);
// fn('okman',12,true); 没有这个函数了这时会报错
这种方法能够随时新增或者删除重载函数,虽然我感觉这个功能好像作用不是太大,但我还是写了.
0. 注意:不要在实例化的时候传参
1. 每次新增了函数是要进行init的
2. 如果要删除的函数是不存在的不会显式的报错,只会低调的在控制台露一面
思路(以下为思路,懂得人和不想了解的人可以走了)
总体来说呢,非常的简单,我是搜了一下网上的重载实现方法都是千篇一律的那种,而且每次写都要手写条件判断多麻烦呀,我就来写个不一样的吧.
言归正传:思路就是建立一个对象数组,这个对象数组呢,结构是这样的:
1.
先按参数的数量大致的分一下类
[参数数量为1的,
参数数量为2的,
参数数量为3的,
参数数量为4的,
… ]
2.
然后数组的每个位置上是一个对象,这个对象呢就用咱们实际的 需要的 重载的 函数的 参数来当对象名.
说着可能麻烦,我将用一个例子来说明上面的两点.
假如我们需要三个重载函数分别是fn(name,score)和fn(name,sex)和fn(name,score,sex)
那么他们的结构就是
[ ,
name:{
score:{fn},
sex:{fn}
},
name:{
score:{
sex:{fn}
}
}
]
这就是大体的思路了,在这个这个数组中,我们使用参数来导航找到对应的方法这样就能确定参数的类型\个数\以及顺序了.
3.
然后另一个使之成为现实的核心方法是eval
,不知道它是啥就某度吧,看w3c文档就行,说的非常明了.
实现
注意:下文使用的方法地图
不是特有名词,是我为了方便解释而进行的比喻
然后我们来大概走一下流程,细枝末节就不说了:
1.大概流程就是generator函数(下文就叫生成器了)可以返回一个方法(默认返回生成的方法),判断没有参数传进来就返回自身对象
2.关于生成的方法,会保存一份生成器里面的对象数组
我就叫她方法地图
了,这个调用重载的过程很象一个在地图上查找路径的过程,我们真正使用的是这个生成的方法
let actObj = function(){
let inArgs=arguments;
let typeForFn =that.actFns;
let l = inArgs.length-1;
let strArg = "";
let fn = typeForFn[l];
for(let i = 0; i<inArgs.length;i++){
strArg +=",inArgs["+i+"]";
if(fn[queryType(inArgs[i])]){
fn = fn[queryType(inArgs[i])]
}else{
console.log('函数不存在: 要调用的函数不存在');
}
}
strArg = strArg.substr(1);//获取参数字符串
let invoca = "fn("+strArg+")";
eval(invoca);
//-------***********
function queryType(arg){
if(arg.constructor == String ){
return "string";
}
if(arg.constructor == Number){
return "number";
}
if(arg.constructor == Boolean){
return "boolean";
}
if(arg.constructor == Array){
return "array";
}
if(arg.constructor == Object){
return "object";
}
if(arg.constructor == Function){
return "function";
}
return "undefined";
}
}
它里面有个根据参数找到的类型的函数,这个也是比较重要的一点,用来生成在方法地图
中查找的路径.
这个方法整个是个什么意思呢?其实就是先根据参数的个数来确定查找的起点(也就是数组的第几个位置),然后用一个对象保存这个起点,遍历参数,根据每个参数生成对应的类型,然后看看能否根据生成的这个类型在方法地图
中找到,找到就遍历下一个参数直到找到函数为止,没找到就报错.
看吧是不是很简单,整个生成器还有个比较重要的地方就是生成方法地图
,是根据传进来的参数,先根据函数的形参个数来生成方法地图
的起点,然后在跟据传进的参数类型数组
进行遍历并生成方法地图
然后将函数放入地图的每条路径的终点.
最核心的已经讲完了~
剩下的细枝末节就自己看看吧.第一次写博客有点小兴奋,(~ ̄▽ ̄)~ ,不足之处请指正.
By wyx0k |2018.8.27
转载请注明出处