利用递归统一化函数参数的不固定数据类型

为了用户调用函数时更方便和灵活,所以我们定义的参数需要不固定数据类型,比如像这样:

mylibs.on(‘event‘,fn);

mylibs.on({
    ‘event1‘:fn1,
    ‘event2‘:fn2,
    ‘event3‘:fn3
});

mylibs.on(‘event1 event2 event3‘,fn);

这是一个自定义事件的例子,有三种传参的方式:1、事件名+回调   2、传递一个对象包含事件名和回调    3、多个事件名+一个回调

你觉得这个函数怎么写才好呢,也许大多数人首先想到的是统一数据类型再处理具体的逻辑,就像下面这样:

//匹配任何空白符,包括\n,\r,\f,\t,\v等(换行、回车、空格、tab等)
var eventSplitter = /\s+/;

var mylibs = {
    on:function(name, callback, context){

        if(typeof name === ‘string‘){
            name = {name:callback};
        }

        if (eventSplitter.test(name)) {
            var names = name.split(eventSplitter),
                name = {};
            for (var i = 0, length = names.length; i < length; i++) {
                name[names[i]] = callback;
            }
        }

        //统一好了数据类型则执行循环(默认数据类型是对象,所以在上面不做判断)
        for(var key in name){
            //具体逻辑......
        }
        return this;
    }
};

这种写法看似不错,先统一处理数据类型,解决了参数不同的问题。不过函数看上去稍显臃肿,如果参数再增加几种类型,那么相应的也会在函数体内增加判断语句,并且把具体的逻辑代码放在for循环内也不是一种优雅的做法。

我们可以尝试一下把判断语句提炼出来,写在一个单独的函数里。

var mylibs = {
    on:function(name, callback, context){

        name = this.eventsApi(name,callback);

        for(var key in name){
            //具体逻辑......
        }
        return this;
    },
    eventsApi:function(name,callback){
        if(typeof name === ‘string‘){
            return {name:callback};
        }
        if (eventSplitter.test(name)) {
            var names = name.split(eventSplitter),
                name = {};
            for (var i = 0, length = names.length; i < length; i++) {
                name[names[i]] = callback;
            }
            return name;
        }
        return name;
    }
};

创建了eventsApi函数来对数据类型做统一处理,很明显on函数已经不臃肿了,2个函数各司其职。但是on函数的具体逻辑代码仍然在for循环内,我们得想办法把它抽离出来。

解决的办法可以利用递归,我们先处理一下eventsApi函数。

eventsApi:function(obj,action,name,rest){
    if (!name) return true;

    if(typeof name === ‘object‘){
        for(var key in name){
            obj[action].apply(obj,[key,name[key]].concat(rest));
        }
        return false;
    }
    if (eventSplitter.test(name)) {
        var names = name.split(eventSplitter);
        for (var i = 0, length = names.length; i < length; i++) {
            obj[action].apply(obj, [names[i]].concat(rest));
        }
        return false;
    }
    return true;
}

是不是眼前一亮,eventsApi函数并没有统一处理数据类型,而是直接调用on函数,此时on函数变成了一个固定数据类型的函数了,其实就是上面第一种传参的形式:一个事件名+回调。

我们看一下完整的代码加深理解吧。

//匹配任何空白符,包括\n,\r,\f,\t,\v等(换行、回车、空格、tab等)
var eventSplitter = /\s+/;

var mylibs = {
    on:function(name, callback, context){
        if (!this.eventsApi(this, ‘on‘, name, [callback, context]) || !callback) return this;
        //具体逻辑......
        return this;
    },
    eventsApi:function(obj,action,name,rest){
        if (!name) return true;

        if(typeof name === ‘object‘){
            for(var key in name){
                obj[action].apply(obj,[key,name[key]].concat(rest));
            }
            return false;
        }
        if (eventSplitter.test(name)) {
            var names = name.split(eventSplitter);
            for (var i = 0, length = names.length; i < length; i++) {
                obj[action].apply(obj, [names[i]].concat(rest));
            }
            return false;
        }
        return true;
    }
};

//调用:
mylibs.on(‘event‘,fn);
mylibs.on({
    ‘event1‘:fn1,
    ‘event2‘:fn2,
    ‘event3‘:fn3
});
mylibs.on(‘event1 event2 event3‘,fn);
时间: 2024-10-17 03:02:42

利用递归统一化函数参数的不固定数据类型的相关文章

javascript中利用柯里化函数实现bind方法

柯理化函数思想:一个js预先处理的思想:利用函数执行可以形成一个不销毁的作用域的原理,把需要预先处理的内容都储存在这个不销毁的作用域中,并且返回一个小函数,以后我们执行的都是小函数,在小函数中把之前预先存储的值进行相关的操作处理即可: 柯里化函数主要起到预处理的作用: bind方法的作用:把传递进来的callback回调方法中的this预先处理为上下文context; /** * bind方法实现原理1 * @param callback [Function] 回调函数 * @param con

Swift # 柯里化函数

前言 此次文章,讲述的是Swift的一个新特性(柯里化函数),可能很多iOS开发人员是第一次听这个词汇,包括我自己也是,自己也用了几天时间才总结出来,希望能帮助到各位咯,个人感觉偏向有开发经验的码友,如果零基础的看懂,希望能给个赞,??! 如果喜欢我的文章,可以关注我,随着后续不断学习Swift中,陆续还会有更新ing.... 什么是柯里化函数? 柯里化(Currying),又称部分求值(Partial Evaluation),是一种函数式编程思想,就是把接受多个参数的函数转换成接收一个单一参数

利用递归把多维数组转为一维数组的函数

函数名称:array_multi2single 函数原形:array array_multi2single(array) 实现功能:把一个多维数组的数值存放到一维数组中,不保存Key. < ?php function array_multi2single($array) {     static $result_array=array();     foreach($array as $value)     {         if(is_array($value))         {    

二值化函数cvThreshold()参数CV_THRESH_OTSU的疑惑【转】

查看OpenCV文档cvThreshold(),在二值化函数cvThreshold(const CvArr* src, CvArr* dst, double threshold, double max_value, int threshold_type)中,参数threshold_type有5种类型: THRESH_BINARY THRESH_BINARY_INV THRESH_TRUNC THRESH_TOZERO THRESH_TOZERO_INV 问题来了:为什么可以在threshold_

JavaScript的柯里化函数

柯里化,或者说部分应用,是一种函数式编程的技术,对于熟悉以传统方式编写 JavaScript 代码的人来说可能会很费解.但如果使用得当,它可以使你的 JavaScript 函数更具可读性. 更具可读性和灵活性 函数式 JavaScript 被吹捧的优点之一就是拥有短小紧凑的代码风格,可以用最少行数.更少重复的代码得到正确的结果.有时这会以牺牲可读性为代价:如果你还不熟悉函数式编程的方法,这种方法写的代码会很难阅读和理解. 如果之前你遇到过柯里化这个术语,但是不知道它是什么意思,把它当做奇怪的.难

TYPESDK手游聚合SDK客户端设计思路与架构之二:安卓平台统一化接口结构及思路

在上一篇<TypeSDK聚合sdk设计基本原则>中我们提到了,设计聚合sdk需要设计开发平台部分的接口,以及设计发布平台的聚合这2个大模块.那么我们今天就先来讲讲发布平台之一:安卓平台的统一化接口结构和思路. 一.相关的需求 安卓平台的统一化接口,我们需要考虑到具体以下的几点: 1.对外需要有统一的接口,保证不同的渠道sdk 对同一个游戏来说,是调用相同的接口,传递相同的参数 2.对内需要有一套扩展性很好的框架,可以应对不同渠道的sdk差异性 二.设计的模块 那么针对这些考虑点,安卓平台的统一

php 递归01 利用递归实现按字典顺序全排列

最近对递归比较感兴趣,所以开始记录一些学习过程中的递归使用方法 1.全排列函数(arrange)有两个参数一个是,需要进行全排列的字符串(假设默认是按字典顺序排列),另一个则是积累的前缀如下图: 第一次默认是'' 第二次,在for循环后分别代表1.2.3并作为下一次子递归的前缀参数 第三次,分别为12 13   21 23   31 32至此需要全排列的剩余字符串长度为一,利用echo进行输出与return结束递归 2.deal函数也有两个参数,分别是当前全排列的字符串,和去除附加在父前缀后剩下

柯里化函数之Javascript

柯里化函数之Javascript 定义 根据定义来说,柯里化就是将一个接收"多个"参数的函数拆分成一个或者许多个接收"单一"参数的函数.定义看起来是比较抽象的,下面来举个例子: 代码 1 2 3 4 5 function concat(str1,str2){ return str1 + str2; } concat("forever","px") // "foreverpx" 不难理解,上面的代码中定义了一

python里的拆包、引用、递归与匿名函数

拆包:*A拆元组,**B拆字典. 引用:在C.C++里面里面a=1,b=a,实际上相当于硬链接,相当于两份a.b各有一个1,多占一个空间,而在python里就是软连接,只有一份,通过id(a)来查看id都一样, 在python里定义的东西如一直没引用,那么就会成为垃圾,不用担心,python有自己的机制去除垃圾. 不可变类型:数字.字符串.元组. 可变类型:列表.字典,它们都不能做字典的key. 递归:一个函数里调用了这个函数自己,递归完成阶乘,3的阶乘=3*(2的阶乘) 递归时一定要想到何时要