cherryvenus

JavaScript 函数参数的最佳实践

原文链接: codeutopia.net

常常会有人问一些像“我应该使用对象来传递函数参数吗”这类问题。我确定你知道这种模式,因为这在许多库中很常见:

`$.post({ a: 'lot', of: 'properties', live: 'in', here: 'yep' })` 

但是我想说,这实际上不是一个好主意。

让我们看看再 JavaScript 中定义函数参数列表最佳的实践。

清晰是目标

把对象当作参数传递给函数的问题是你要传入的值指向不清。

像jQuery这样的库,这不是一个大问题——就是看看文档然后就知道你要怎么做了。但是在自定义代码中,你会在工作中碰到?看看文档……啊!等等!哪来的文档!Yeah。

尽可能多的文档当然好,自定义代码库永远不会有这种你需要的文档。如果你需要弄清一些方法需要的参数,你需要深入了解源代码。

这个问题可以用一个例子来解释。这里有一个用于控制其他函数之间间隔的函数。

function throttle(o) {
  o.threshhold || (o.threshhold = 250);
  var last,
      deferTimer;
  return function () {
    var context = o.scope || this;

    var now = +new Date,
        args = arguments;
    if (last && now < last + threshhold) {
      // hold on to it
      clearTimeout(deferTimer);
      deferTimer = setTimeout(function () {
        last = now;
        o.fn.apply(context, args);
      }, o.threshhold);
    } else {
      last = now;
      o.fn.apply(context, args);
    }
  };
} 

(源代码参考 Remy Sharp)

快!你往参数对象中放入了什么值?

没有浏览整个函数的话,你是找不到关于参数的恼人线索的,甚至之后,你可能会出错。

这里是原始的注释:

`function throttle(fn, threshhold, scope)`

快!函数取了什么参数??

如果你拆解了这个炸弹,然后你需要回答那个问题或者爆炸,并且你只有几秒钟的时间思考……我肯定具有独立的参数会比较好。

好啦,那么第一个最佳尝试是:给你的函数使用单独命名的参数

此外,单独的参数如果在一个函数式编程风格中同样很有帮助。在FP中一个常见的事是调用函数,如果你的函数使用对象作为参数,那么会非常苦逼。

对于可选项来说对象非常好

单独的参数是一个很好的开始,那么我们将会在函数有可选参数的时候遇到问题。

对于只有一个可选参数的函数来说,解决方案很简单:

function stuff(requiredA, requiredB, optional)

最佳实践:可选项放在最后

然而,如果你有多于一个的可选项,问题……如果我们只传_一_个可选参数如何?

function foo(required, optionalA, optionalB) { /* something */ }

foo('hello', undefined, 1337); 

啊!需要填充类似于undefined的值,这真的是越来越复杂了——同样需要调用他们,并且需要在函数中处理这些参数

在多可选参数的情况下,我们可以使用对象。这并没有真正地解决问题,但是这意味着我们不需要处理额外的值。

foo('hello', { optionalB: 1337 });

最佳实践:如果有超过一个可选参数时,使用对象

ES6的好处

最后,如果你使用ES6或者TypeScript,我们可以使用一些新的语法。

对于有可选参数的函数,我们并不清楚这个参数是否可选。有了ES6,我们能够提供一个默认值让这个变得明显:

function something(a, b = 'default value') {
  //if b is not passed, its value becomes 'default value'
} 

最佳实践:给可选参数提供默认值

我们可以使用解构参数列表中对象和它的默认值。

function foo(required, { optionalA, optionalB } = { }) {
  //available variables:
  //required, optionalA, optionalB
}

foo('hi', { optionalA: 1, optionalB: 1337 }); 

这给了你两全其美的办法。你可以方便地使用对象作为参数,但是对象的属性能够从函数的注释中看到。

最佳实践:使用ES6解构重要的地方

总结

对于上述建议的补充,在函数声明前使用注释是另一个有用的习惯。这对于读者来说非常有用,能够迅速了解大概,不仅仅是函数的参数,还有他们的类型和其他注意点。

总结:

  • 使用单独的命名参数

  • 把可选参数放在最后

  • 对于多个可选参数适用对象

  • 默认值可以用来表明可选参数

  • 使用解构来说明对象参数

这是另一个你可以用ES6默认参数做的有趣事情。通常情况下,如果你不传递一个必要的参数,什么事都不会发生。但是有了默认值,我们可以让代码自动抛出一个缺少必要参数的错误!David Walsh有一个对此的极佳总结