JavaScript 函数参数的最佳实践
常常会有人问一些像“我应该使用对象来传递函数参数吗”这类问题。我确定你知道这种模式,因为这在许多库中很常见:
`$.post({a:'lot',of:'properties',live:'in',here:'yep'})`但是我想说,这实际上不是一个好主意。
让我们看看再 JavaScript 中定义函数参数列表最佳的实践。
以清晰为目的
把对象当作参数传递给函数的问题是你要传入的值指向不清。
像jQuery这样的库,这不是一个大问题——无非看看文档然后就知道你要怎么做了。但是你在工作中看别人写的代码呢?查文档……啊!等等!哪来的文档!Yeah。
尽可能多的文档当然好,山寨的代码库永远不会有那种你需要的文档。如果你需要弄清某个方法需要哪些参数,你就得深入了解源代码。
这个问题可以用一个例子来解释。这里有一个用于对其他函数的调用频率进行限制的函数。
functionthrottle(o){o.threshhold||(o.threshhold=250);varlast,deferTimer;returnfunction(){varcontext=o.scope||this;varnow=+newDate,args=arguments;if(last&&now<last+threshhold){// hold on to itclearTimeout(deferTimer);deferTimer=setTimeout(function(){last=now;o.fn.apply(context,args);},o.threshhold);}else{last=now;o.fn.apply(context,args);}};}(源码借鉴了Remy Sharp)
快说!你可以往参数对象中传什么值?
没有通读整个函数的话,你是找不到关于参数的恼人线索的,甚至你已经读过全部代码之后,还可能会出错。
这是代码原来的函数签名:
functionthrottle(fn,threshhold,scope)快说!函数接受哪些参数?
如果你正在拆弹,你需要回答那个问题否则炸弹就会爆炸,而你只有几秒钟的时间思考……我肯定你会觉得有独立的参数的这一版比较好。
好啦,那么第一个最佳实践是:给你的函数使用独立命名的参数
此外,独立的参数如果在一个函数式编程风格中同样很有帮助。在 FP 中经常会部分调用函数(partially apply functions 又叫做偏函数应用,即对一个函数传递一部分参数,然后返回一个新的函数,这个函数接收剩余的参数——译者注),如果你的函数使用对象作为参数,那么会很难做到部分调用。
使用对象参数对于可选项来说挺合适
单独的参数是一个很好的开始,那么我们将会在函数有可选参数的时候遇到问题。
对于只有一个可选参数的函数来说,解决方案很简单:
functionstuff(requiredA,requiredB,optional)
最佳实践:可选项放在最后
然而,如果你有多于一个的可选参数,那么问题来了……如果我们只需要传一个可选参数要怎么做?
functionfoo(required,optionalA,optionalB){/* something */}foo('hello',undefined,1337);啊!需要填充类似于undefined的值,这让函数不管是调用还是处理参数都变得更复杂了。
在多个可选参数的情况下,我们可以使用对象。这并没有完全解决参数意义不清晰的问题,但这带来的好处是我们不需要传递和处理额外的值。
foo('hello',{optionalB:1337});
最佳实践:如果有超过一个可选参数时,使用对象
ES6的好处
最后,如果你使用ES6或者TypeScript,我们可以使用一些新的语法。
对于有可选参数的函数,我们并不清楚这个参数是否可选。有了ES6,我们能够提供一个默认值让这个变得明显:
functionsomething(a,b='default value'){//if b is not passed, its value becomes 'default value'}最佳实践:给可选参数提供默认值
我们也可以在参数列表中同时使用对象解构和默认值。
functionfoo(required,{optionalA,optionalB}={}){//available variables://required, optionalA, optionalB}foo('hi',{optionalA:1,optionalB:1337});这给了你两全其美的办法。你可以方便地使用对象作为参数,但是对象的属性能够从函数签名中看到。
最佳实践:当有必要时使用ES6解构
总结
对于上述建议的补充,在函数声明前使用注释块是另一个有用的习惯。这对于阅读函数的人来说非常有用,有了注释,他们能够迅速了解函数的大致信息,不单是函数的参数,还有它们的数据类型和其他注意点。
总结:
使用独立命名的参数
把可选参数放在最后
在有多个可选参数的情况下使用对象
用默认值来表明可选参数
使用解构澄清对象属性
你还可以用 ES6 默认参数做另一件有趣的事情。通常情况下,如果你不传递一个必要的参数,什么事都不会发生。但是有了默认值,我们可以让代码自动抛出一个缺少必要参数的错误!David Walsh 对此技巧有一个极佳的总结。