[探索]怎么样的参数能让 JS - API 更灵活

62,956 阅读9分钟

外在决定是否需要了解内在,内在决定是否会一票否决外在。内外结合,好上加好。

1.前言

开发 API 的时候,把参数的名字和位置确定下来,函数定义就可以说是完成了。因为 API 使用者来说,只需要知道如何传递参数,以及函数将返回什么样的值就够了,无需了解内部。所以参数多多少少影响了 API 的一个灵活程度和使用复杂程度。在设计 API 的时候,应该怎么设计参数,下面就简单的写下,如果大家有不同的想法,欢迎在评论区留言。

下面使用的例子,除了原生了 JQuery 的 API。其他的例子都是取自自己封装的一个常用函数库 ecDo 。欢迎提建议和 star。

2.什么时候该设置参数

其实什么时候设置参数并没什么什么说法,规范。只要觉得用参数会使得 API 的使用会更加的简单和灵活就用了。

设置参数,可能一开始不会有很好的灵感,但是在使用过程中,用得难受了,自然会像办法优化。

比如 有 ecDo.count(array|string,item) 这个 API 就是统计一个数组的每个元素的出现次数或者字符串每一个字符的个数。

这很容易实现,代码也贴下

count(arr) {
    let obj = {}, k, arr1 = []
    //记录每一元素出现的次数
    for (let i = 0, len = arr.length; i < len; i++) {
        k = arr[i];
        if (obj[k]) {
            obj[k]++;
        } else {
            obj[k] = 1;
        }
    }
    //保存结果{el-'元素',count-出现次数}
    for (let o in obj) {
        arr1.push({el: o, count: obj[o]});
    }
    return arr1;
},



let strTest1='abbccc'
console.log(ecDo.count(strTest1));
//result:[{el: "a", count: 1},{el: "b", count: 2},el: "c", count: 3}]

但是有些时候,开发者并不需要知道所有元素的个数,比如就需要知道 'a' 的出现次数,这个情况直接返回 'a' 的个数就行了,不需要像上面例子一样,返回一个数组。这样用起来会舒服一些,改起来也很简单,只要增加一个参数,一个判断即可。

count(arr,item) {
    //重复代码略
    return item?obj[item]:arr1;
},



let strTest1='abbccc'
console.log(ecDo.count(strTest1),'a');
//result:1

还有一个常用的 API 是ecDo.clearKeys(obj)--清除对象中值为‘空’(null, undefined和'')的属性。

这个也很容易实现,

clearKeys(obj) {
    let _newPar = {};
    for (let key in obj) {
        if (obj[key]===0||obj[key]===false||obj[key]) {
            _newPar[key] = obj[key];
        }
    }
    return _newPar;
},


ecDo.clearKeys({1:0,2:2,3:''})
//result:{1: 0, 2: 2}

想必大家也发现这样写法太不灵活了,如果下次要把 0false 的属性也清空呢?如果下次要把值为 '--' 的属性也清空呢?这样就做不到了,所以还要改一下,增加一个参数 clearValues - 待清空的值。

要使用的一个函数

clearKeys(obj, clearValues = [null, undefined, '']) {
    clearValues.forEach((item, index) => {
        clearValues[index] = Number.isNaN(item) ? 'NaN' : item
    });
    let _newPar = {};
    for (let key in obj) {
        //checkValue 看下面定义
        if (!checkValue(obj[key], clearValues)) {
            _newPar[key] = obj[key];
        }
    }
    return _newPar;
},


ecDo.clearKeys({a:'',b:0,c:11})
//result:{b: 0,c: 11}
ecDo.clearKeys({a:'',b:0,c:'--'},['--',''])
//result:{b: 0}
ecDo.clearKeys({a:'',b:0,c:11,d:false},[0,''])
//result:{c: 11,d: false}
ecDo.clearKeys({a:NaN,b:2,c:undefined},[NaN,undefined])
//result:{b: 2}

checkValue 函数

function checkValue(val, vals) {
    let _val = val;
    if (Number.isNaN(val)) {
        _val = 'NaN'
    }
    return vals.indexOf(_val) !== -1;
}

这样以来,想清除任何值的属性,这里都可以实现。

3.参数数量和前置

这两个注意点可以说是平常接触最频繁的,也是最无可辩解的。

首先参数的数量,在不影响 API 使用的情况下肯定是能少就少,越少越好。因为参数的越少,API 的记忆成本越低,调用也更加便利。

参数能少就少,越少越好,是有前提的--不影响 API 的使用。如果多个参数, API 使用能更方便,灵活,简单。多个参数就多个参。

然后参数的前置性,就是参数相关性越高,越不能省略的,就越要放在前面。虽然可以把可省略参数放后面,但是这样问题可能会很多。

4.使用对象作为参数

什么时候该使用对象作为函数的参数,暂时发现是两种情况。

1.参数过多的时候

2.参数不固定的时候

比如 ajax 的参数,至少至少也有 5 个。url-请求链接,method-请求方式,data-请求参数,success-成功回调,fail-失败回调。如果不使用对象作为参数,完整的写法,是下面这样

ajax(url,method,data,success,fail)

但这5个参数里面,除了 url ,其他参数都可以省略或者默认。如果只需要传 url 和 success,需要像下面这样写

ajax(url,'','',success)

因为参数的顺序不能乱,所以就要这样写。这样多难受就不说了,如果参数过多,参数看着会很长,容易搞错参数顺序,就会出现问题了。

如果使用对象传参,就舒服很多了。

ajax({url:url,success:function(){}})

这样写的会多一点,但是好处也有。首先用户还是需要记住参数名,但不用关心参数顺序,不用担心参数过多。然后就是开发者想增加多少参数都比较方便,也不用关心参数后置的问题。

是否使用对象作为参数,判断的指标应该只有一个:是否方便使用,灵活。

5.参数默认值

什么时候应该设计默认值?也分几种情况讨论

首先是,一个参数值出现频率比其他情况大的时候。比如有一个 ecDo.encrypt(str,regIndex,repText) 的 API,作用很简单,就是对字符串的特定位置进行加密。比如经常会遇到隐藏用户的手机号码的需求。

电话号码随便编造的


ecDo.encrypt('18819233362','3,7')
//result:188*****362

ecDo.encrypt('18819233362','3,7','+')
//result:188+++++362

ecDo.encrypt('18819233362','4')
//result:*****233362

ecDo.encrypt('18819233362','-4')
//result:188192*****

在这个 API 里面 ,第三个参数 repText 可能大多数情况都是使用 * 。如果不对 repText 设置默认值,如果每一次都传一个 * ,不但写的多,看得也难受。

还有一种情况,从特定位置执行到结尾结束的时候。比如原生的 substr(start,length) ,第一个参数是开始字符的下标,第二个参数是截取的长度。如果省略,就从开始位置截取到结尾。这样就算是一种方便。

5.参数多态

这里说的参数多态跟继承方面的多态有点区别

参数多态这个很常见,目的很简单,就是通过不同的传参方式让一个函数进行不同的操作。看到这里,可能大家一下子就想到 splice。因为一个 splice 可以实现数组的增加,删除,替换

//删除
let arr=[1,2,3,4,5,6]
arr.splice(0,1)
//result:arr:[2, 3, 4, 5, 6]

//替换
arr=[1,2,3,4,5,6]
arr.splice(2,1,0)
//result:arr:[1, 2, 0, 4, 5, 6]

//增加
arr=[1,2,3,4,5,6]
arr.splice(2,0,0)
//result:arr:[1, 2, 0, 3, 4, 5, 6]

但是 splice 应该并不算是参数多态,只能算是一些技巧的一个写法。

表现出参数多态的,比如 JQuery 的 attr 。既可以获取属性的值,也可以设置属性的值。

//获取 dom 元素 id 的值
$(dom).attr('id')
//设置 dom 元素 id 的值
$(dom).attr('id','domId')

JQuery 把多态表现得更好的应该是 $() 。JQuery 火的原因,跟这个 $() 有很大的关系,只要是合法的选择器,页面存在这个元素,就能找到。让选择元素变得非常简单。

关于 $() 的强大特性,可参考 jQuery 选择器

在自己封装 API 的时候,也会遇到操作 cookie 的一系列操作(设置,获取,删除)。之前是这样

ecDo.setCookie(cookieName,value,day)//设置(cookie名称,cookie值,有效的天数)
ecDo.getCookie(cookieName)//获取
ecDo.removeCookie(cookieName)//删除

现在是这样

ecDo.cookie(cookieName,value,day)//设置(cookie名称,cookie值,有效的天数)
ecDo.cookie(cookieName)//获取
ecDo.cookie(cookieName,value,-1)//删除

这样使用起来,就比之前简单一点,相当于只是记住一个 API 就可以了。

参数的多态,就是让 API 的指责会根据参数的情况进行改变。相当于把相似职责的 API 给合并成一个。不需要给用户提供出太多 API,方便用户的使用。即使这样可能违法了单一指责原则,但是呼应了最少知识原则。能让 API 的使用尽可能简单化了。

5-1.什么时候不该设置参数多态

参数多态就是把相似职责的 API 给合并成一个。但是有时候并不适合使用。更适合把合并的 API 拆分成几个。

比如之前封装常用 API 的时候。有一个去除字符串空格的 API : ecDo.trim(str,type) (str-待处理的字符串,type-去除空格的类型----1-左右空格,2-所有空格,3-左空格,4-右空格) 。

使用形式

ecDo 是我封装的一个常用函数库。

let str=' 守 候 ';
console.log(ecDo.trim(str,1));//'守 候'
console.log(ecDo.trim(str,2));//'守候'
console.log(ecDo.trim(str,3));//'守候 '
console.log(ecDo.trim(str,4));//' 守候'

这样使用的话,想必大家都看到问题了。1 2 3 4 是什么鬼?就算知道是什么鬼,也要把这几个鬼记住。记忆成本大,调用也不方便。后来就拆分成四个函数了。

let str=' 守 候 ';
console.log(ecDo.trim(str));//'守 候'
console.log(ecDo.trimAll(str));//'守候'
console.log(ecDo.trimLeft(str));//'守候 '
console.log(ecDo.trimRright(str));//' 守候'

这样虽然多了几个 API 但是使用起来简单了。记忆成本也比之前少。是否设置参数多态,还是要看调用的情况而言。

6.小结

好了,关于 API 参数方面的东西就暂时写到这里了,这部分的内容不多,所以篇幅不是很长,也容易理解。但是参数这个话题也相对开放,如果大家有什么好的想法。欢迎在评论区留言。

-------------------------华丽的分割线--------------------

想了解更多,和我交流,内推职位,请添加我微信。或者关注我的微信公众号:守候书阁