JavaScript重载函数的实现【重构优化版】

您所浏览的网站已严重侵犯了原文作者nivk的权益。

本文由nivk撰写,摘抄自nivk的博客:http://blog.csdn.net/teajs

前言:为什么我们需要JavaScript重载函数?


一把剪刀可以用来做什么?




剪刀可以用来剪纸,也可以用来剪鱼。

现在,我们把剪刀剪东西这个动作封装成一个函数。

1、假设剪“纸”是一个String类型。

2、假设剪“肉”是一个Boolean类型。

现在,我们来实现一下。

function cut(obj) {
    if (typeof obj === "string") {
        console.log("我们在剪纸");
    }
    else if (typeof obj === "boolean") {
        console.log("我们在剪肉");
    }
}
没错,现在我们调用这个“cut”函数是可以满足我们的需要的。

甚至有人说,我可以做优化!比如这样:

function cut(obj) {
    switch (obj) {
        case "string":
            console.log("我们在剪纸");
            break;
        case "boolean":
            console.log("我们在剪肉");
            break;
    }
}
但是,这种程度的“优化”和本文是没有关系的。


我们需要考虑的是更复杂的情况,如果我需要一剪刀下去,同时剪了“纸”和“肉”,这时才能达到某个神秘成就。我们该如何做?

OK,我们先列举一下都有哪几种情况:

1、仅仅剪了“纸”。

2、仅仅剪了“肉”。

3、同时剪了“纸”和“肉”。

4、。。。。

等等!为什么有第四种情况?

嗯……你应该考虑一下,同时剪的时候,是先剪了“纸”,还是先剪了“肉”。


所以,我们重新列举一遍:

1、仅仅剪了“纸”。

2、仅仅剪了“肉”。

3、同时剪了“纸”和“肉”,并且先剪了“纸”。

4、同时剪了“纸”和“肉”,并且先剪了“肉”。


好的,我们用代码来实现一遍:

function cut(obj, obj1) {
    if (typeof obj === "string" && typeof obj1 === "boolean"){
        console.log("同时剪了纸和肉,并且先剪了纸");
    }
    else if (typeof obj === "boolean" && typeof obj1 === "string"){
        console.log("同时剪了纸和肉,并且先剪了肉");
    }
    else if (typeof obj === "string") {
        console.log("我们在剪纸");
    }
    else if (typeof obj === "boolean") {
        console.log("我们在剪肉");
    }
}
有同学有优化方案,但这同样不是我们要关注的地方。

重点是,这么多判断,你写着不累,我看着都累啊。

而且这仅仅是4种情况,真实项目环境情况可能比复杂的多。



构思:换一种可读性更好的方法来应对函数的“重载”。


如何使因参数变化而变化的函数内部实现更优雅?


要想实现方式优雅且更有实用性,有几个问题亟需解决:

1、去掉函数内部的类型判断。

2、加强函数参数的类型判断。

3、允许某个参数的泛类型。

4、允许限制某个参数的枚举类型。


关于以上4点的具体解释如下:

1、去掉函数内部的类型判断。

此条无需过多解释,所有优化点都围绕着这一点展开。

2、加强函数参数的类型判断。

可能有人觉得和第1条有所冲突,其实并不是。

第1条是将判断写在了函数内,而此条则是将其移到函数外去做。

3、允许某个参数的泛类型。

何为泛类型?因为JavaScript是弱类型语言,所以我们一直使用的都是泛类型。

但是既然我们要增加类型判断,也不能一棒子打死。

所以我们还是需要支持泛类型的。

4、允许限制某个参数的枚举类型。

这里我用“枚举类型”不是很合适,其实我想表达的意思是,某个参数可以是一个或多个限定范围的类型。


现在我提出一种用法:

var fn = Overload.create().
	 add("Number", function (num) {
             console.log("数字:" + num);
         }).
         add("String", function (str) {
             console.log("字符串:" + str);
         }).
         add("Number, String", function (num, str) {
             console.log("数字:" + num + "字符串:" + str);
         }).
         add("String, Number", function (str, num) {
             console.log("字符串:" + str + "数字:" + num);
         }).
         add("String || Number, Boolean", function (val, bool) {
             console.log("字符串或数字:" + val + "布尔值:" + bool);
         });
一眼望去是不是干净整洁了许多?而且在支持代码块折叠的编辑器中会如此显示:


非常容易找到你想了解的函数实现区域。




废话不多说,直接上代码!

!function () {

    /* 私有变量 */

    var any = "[object Unkonw]";



    /* 私有方法 */

    function getType(str) {
        /// <summary>根据字符串获取Js可用于判断的类型</summary>
        /// <param name="str" type="String"></param>
        /// <returns type="any" />

        if (!str || !(str = str.toString().trim())) {
            // 容错,传入了空字符串

            return null;
        }

        switch (str) {
            case "Number": case "number":
            case "String": case "string":
            case "Boolean": case "boolean":
            case "Function": case "function":
            case "Object": case "object":
                return str.toLowerCase();
            case "Any": case "any": case "*":
                return any;
            case "Null": case "null": case "undefined":
                throw new Error("Invalid type");
            default:
                return eval(str);
        }
    }

    function processParameters(_parameters) {
        /// <summary>
        /// 参数类型处理
        /// </summary>

        var parameters;
        var parameter;

        for (var i = _parameters.length; i--;) {
            // 遍历所有重载参数列表

            parameters = _parameters[i];

            if (parameters === null) {
                // 跳过不需要参数的重载函数

                continue;
            }

            for (var x = parameters.length; x--;) {
                // 遍历某个重载的参数列表

                parameter = parameters[x];

                if (parameter instanceof Array) {
                    // 判断参数是否存在或者判断

                    for (var n = parameter.length; n--;) {
                        // 获取或者判断中每个参数的类型

                        parameter[n] = getType(parameter[n]);
                    }
                } else {
                    // 不存在或者条件,直接获取参数类型

                    parameters[x] = getType(parameter);
                }
            }
        }
    }



    /* 公开静态方法 */

    window.Overload = {
        create: function () {
            /// <summary>创建重载对象</summary>
            /// <returns type="Function" />

            var _parameters = [];
            var _functions = [];
            var isProcessed = false;

            function Overload() {
                /// <summary>调用重载函数</summary>

                if (_functions.length === 0) {
                    // 检查是否有可调用函数

                    throw new Error("Function not implemented");
                }

                if (!isProcessed) {
                    // 检查所有参数是否经过了类型处理

                    processParameters(_parameters);
                    isProcessed = true;
                    delete Overload.add;
                }

                var parameters;

                for (var i = 0, len = _functions.length; i < len; i++) {
                    parameters = _parameters[i];

                    if (!parameters && !!arguments.length ||
                        !!parameters && arguments.length !== parameters.length) {
                        // 跳过参数数量不一致的重载(不包括参数列表为空的情况)

                        continue;
                    }

                    var checkDone = true;

                    if (parameters !== null) {
                        for (var x = 0, xLen = parameters.length; x < xLen; x++) {
                            // 遍历参数类型

                            var checkType;
                            var checkTypeof;
                            var argTypeof = typeof arguments[x];

                            if (parameters[x]._isOrParameters) {
                                // 检查并跳过或者判断参数类型不一致的重载

                                for (var n = 0, nLen = parameters[x].length; n < nLen; n++) {
                                    checkType = parameters[x][n];
                                    checkTypeof = typeof checkType;

                                    if ((checkTypeof === "string" && argTypeof !== checkType ||
                                        checkTypeof !== "string" && !(arguments[x] instanceof checkType)) &&
                                        checkType !== any) {
                                        if (n + 1 == nLen) {
                                            // 找不到任何匹配的参数

                                            checkDone = false;
                                            break;
                                        }
                                    } else {
                                        // 找到了匹配参数,直接进入下一步

                                        break;
                                    }
                                }
                            } else {
                                // 检查并跳过参数类型不一致的重载

                                checkType = parameters[x];
                                checkTypeof = typeof checkType;

                                if ((checkTypeof === "string" && argTypeof !== checkType ||
                                    checkTypeof !== "string" && !(arguments[x] instanceof checkType)) &&
                                    checkType !== any) {
                                    checkDone = false;
                                    break;
                                }
                            }
                        }
                    }

                    if (checkDone) {
                        return _functions[i].apply(this, arguments);
                    }
                }

                throw new Error("Invalid parameter");
            };

            Overload.add = function (str, fun) {
                /// <summary>添加重载函数</summary>
                /// <param name="str" type="String">参数列表字符串</param>
                /// <param name="fun" type="Function">重载调用的函数</param>
                /// <returns type="OverloadFunction" />

                var parameters = null;
                if (typeof str === "string") {
                    parameters = str.trim().split(",");
                    for (var i = parameters.length; i--;) {
                        if (parameters[i].indexOf("||") >= 1) {
                            parameters[i] = parameters[i].split("||");
                            parameters[i]._isOrParameters = true;
                        }
                    }
                }

                _parameters.push(parameters);
                _functions.push(fun);

                return Overload;
            };

            return Overload;
        }
    };

}();

若您有任何想要详细了解的地方可以在本文最下方评论留言,我会在看到您的留言后第一时间给您回复!大笑

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值