03 April 2013

原文:ECMAScript 6 and Default Argument

许多编程语言都支持将某个函数参数声明为默认参数这一概念,这样调用者不需要总是指定该参数的值。不幸的是,JavaScript 的语法不支持默认参数。随着即将到来的 ECMAScript 6,这种情况可能很快会改变。

一些 JavaScript 程序员使用各种不同的运行时技巧实现某个参数的默认值效果。常用方法利用了这一事实:如果某个参数没有给定值,那么就是简单 undefined

function foobar(a) { return typeof a; }
foobar(); // "undefined"

这可能会导致一些像这样的代码:

function runApp(appName) { console.log('Running', appName || 'AUTOEXEC.BAT'); }

如果只调用 runApp()(不带任何参数),该函数将打印 Running AUTOEXEC.BAT。使用的逻辑或表达式(操作符 ||,查看二进制逻辑运算符 上的 11.11 节)表示,如果左侧(appName)是 true(查看 ToBoolean 上的 9.2 节),则短路生效,否则返回右边(AUTOEXEC.BAT)。注意,如果执行该函数时使用诸如 null0![]、甚至 ~~{} 这样的参数值,结果是一样的。

经常遇到的另一种变体是真正的检测参数类型。现在我们可以区分 undefined 和其他类型。因此,可以使用一个合适的替换值。

function runApp(appName) {
  if (typeof appName === 'undefined') appName = 'AUTOEXEC.BAT';
  console.log('Running', appName);
}

在其他情况下,我们事实上真的需要知道该函数是否用一定数量的参数被调用。为了这个目的,arguments 对象就派上用场了。

function runApp(appName) {
  if (arguments.length === 0) appName = 'AUTOEXEC.BAT';
  console.log('Running', appName);
}

一旦语法本身支持默认参数,所有这一切花哨的技巧不再是必要的。在最新的 ECMAScript 6 草稿13 节,提到(函数)的正式参数不再是一个简单的标识符列表(正如 ECMAScript 5 中定义的),因为参数被泛化为允许 BindingElement。虽然这种新结构允许对象或数组模式(参见我之前关于 destructuring assignment 的博客文章),重要的是要认识到 BindingElement 支持一个可选的初始化,就像在一个变量声明。

说得明白点,这意味着一个函数声明可以为每个参数指定一个默认值。之前的 runApp 函数会变得如此简单:

function runApp(appName = 'AUTOEXEC.BAT') {
  console.log('Running', appName);
}

在等待浏览器和 JavaScript 引擎实现该功能期间,目前可以在 TraceurTypeScript 的帮助下使用这样的结构。有趣的是脱糖(降级)方式的不同,Traceur 将使用 arguments 对象,而 TypeScript 执行 undefined 类型检测。

内置语法支持默认参数实在是太棒了。一个 JavaScript 编辑器可以提供更好的帮助内容(自动补全)。一个代码分析器将能够跟踪省略了无默认值参数的函数调用。我不能等到一个 linter 向我抱怨:

guide.js:42 Specify the non-optional parameter 'stop' to function 'createSeries'
为函数 'createSeries' 指定必选参数 'stop'

更好的语言工具无疑将尽可能的减少任何代码错误



blog comments powered by Disqus