11.5 模拟命名参数

在编程语言中调用一个函数(或者方法)的时候,你必须将实参(调用者指定的)映射到形参(函数中定义的)。有两种方式来完成这种映射:

  • 位置相关的参数通过位置来映射。第一个实参映射到第一个形参,第二个实参映射到第二个形参,以此类推。

  • 命名参数使用名字(标签)来实现这种映射。名字和在函数定义中的形参关联起来,在函数调用中给实参打上标记。命名参数以何种顺序出现并不重要,因为它们被正确标记了。

命名参数主要有两个好处:在函数调用中它们提供了参数描述,同时对于可选对象也能轻松应对。我将会首先介绍这些好处,然后向你展示在 JavaScript 中如何通过对象字面量模拟命名参数。

11.5.1 作为描述的命名参数

只要一个函数有超过一个的参数,你可能就会对每一个参数用来做什么感到迷惑。例如,假设有一个函数 selectEntries() ,返回一组来自于数据库的条目。给出函数调用:

selectEntries(3, 20, 2);

这两个数字是什么意思? Python 支持命名参数,所以在 Python 中可以很轻松地说明发生了什么:

# Python syntax
selectEntries(start=3, end=20, step=2)

11.5.2 可选的命名参数

可选的位置相关的参数,仅在位于形参末尾的时候被省略才会正常工作。其它情形,必须插入占位符(比如 null ),以便于剩下的参数能正确对上位置。

对于可选的命名参数来说,这不是问题。你可以轻易地省略任何一个参数。下面有一些例子:

# Python syntax
selectEntries(step=2)
selectEntries(end=20, start=3)
selectEntries()

11.5.3 在 JavaScript 中模拟命名参数

对于命名参数, JavaScript 并没有像 python 和其它很多语言一样本地支持。但是有一种合理的优雅的模拟方式:通过对象字面量的命名参数,以单个实参的形式传入。当你使用这种方式的时候,一次 selectEntries() 的调用看起来像这样:

selectEntries({ start: 3, end: 20, step: 2 });

函数接收到一个对象,该对象有 startendstep 属性。你可以省略任何一个:

selectEntries({ step: 2 });
selectEntries({ end: 20, start: 3 });
selectEntries();

在 ECMAScript 5 中,你可能像下面这样实现 selectEntries()

function selectEntries(options) {
    options = options || {};
    var start = options.start || 0;
    var end = options.end || getDbLength();
    var step = options.step || 1;
    ···
}

在 ECMAScript 6 中,你可以使用解构,看起来像这样:

function selectEntries({ start=0, end=-1, step=1 }) {
    ···
}

如果你调用 selectEntries() 不传入参数,解构就会失败,因为你不可能让一个对象模式匹配上 undefined 。这个问题可以通过默认值来修复。在下面的代码中,如果没有传入任何参数的话,对象模式会匹配上 {}

function selectEntries({ start=0, end=-1, step=1 } = {}) {
    ···
}

你也可以将位置相关的参数和命名参数结合起来。通常的做法是把命名参数放在最后:

someFunc(posArg1, { namedArg1: 7, namedArg2: true });

原则上, JavaScript 引擎会优化这种模式,这样就不会创建中间对象,因为调用方的对象字面量和函数定义处的对象模式都是静态的。

在 JavaScript 中,此处展示的命名参数形式有时被称为可选项或者可选对象(比如, jQuery 文档)。

results matching ""

    No results matching ""