在 ES6 中将 NULL 作为参数传递时,在提供时不使用默认参数

Passing in NULL as a parameter in ES6 does not use the default parameter when one is provided

为什么在 ES6 中将 null 作为参数传递时不使用默认参数的已知原因是什么?

function sayHello(name = "World") {
    console.log("Hello, " + name + "!");
}

sayHello("Jim");     // Hello, Jim!
sayHello(undefined); // Hello, World!
sayHello(null);      // Hello, null!

null 是不会触发使用默认值的值,当参数为 undefined.

时将使用默认值

规范中就是这样定义默认参数的。参见 MDN:(强调我的)

Default function parameters allow formal parameters to be initialized with default values if no value or undefined is passed.

null既不是无值,也不是undefined,所以它不会触发默认初始化。

这不是那么明显

我读过一些关于为什么 undefinednull 完全不同的评论,这就是为什么它解释了默认参数的当前行为。

有人可能会争辩说显式传递未定义的 不应该 触发默认值替换,因为当我有一个函数时:

const f = (x = 'default') => console.log(x);

我希望它在 运行 时打印 "default" 为:

f();

但我希望它在我明确 运行 时打印 "undefined" 为:

f(undefined);

否则我为什么要首先使用 f(undefined)?很明显,我在这里的目的是提供一些论据,而不是将其排除在外。

相反的例子

现在,考虑这个函数:

const g = (...a) => console.log(JSON.stringify(a));

当我将它用作:

g();

我得到:[]

但是当我将它用作:

g(undefined);

我得到:[null]

这清楚地表明:

  1. 传递 undefined 与根本不传递参数不同
  2. 有时 null 可以是 默认值而不是 undefined

TC39 决定

关于默认参数当前行为的一些背景可以在 2012 年 7 月 24 日的 TC39 会议记录中看到:

顺便说一下,它表明显式传递 undefined 最初 并没有 触发初稿中的默认值,并且讨论了是否应该这样做那。因此,正如您所看到的,当前的行为对于 TC39 成员来说并不像现在对在这里发表评论的人来说那么明显。

其他语言

话虽如此,决定什么应该和什么不应该触发默认值替换最终完全是任意的。如果你仔细想想,即使有一个单独的 undefinednull 也会觉得很奇怪。有些语言只有 undefined(如 Perl 中的 undef),有些只有 null(如 Java),有些语言使用等价的 false 或一个空的列表或数组(比如 Scheme,你可以有一个空列表或 #f (false) 但没有 null 的等价物可以区别于空列表和假值)和有些语言甚至没有 nullfalseundefined 的等价物(比如 C 使用整数而不是 truefalse 以及一个 NULL 指针实际上是一个指向地址 0 的普通指针——即使被任何测试空指针的代码映射时也无法访问该地址。

你能做什么

现在,我可以理解您需要用默认值替换 null。不幸的是,这不是默认行为,但您可以创建一个简单的函数来帮助您:

const N = f => (...a) => f(...a.map(v => (v === null ? undefined : v)));

现在,每次您想要用默认值代替 null 值时,您都可以像这样使用它。例如。如果您从上述示例之一获得此功能:

const f = (x = 'default') => console.log(x);

它会为 f()f(undefined) 打印 "default" 但不会为 f(null) 打印。但是当你使用上面定义的N函数来定义f函数时,像这样:

const f = N((x = 'default') => console.log(x));

现在 f()f(undefined) 而且 f(null) 打印 "default".

如果您想要稍微不同的行为,例如将默认值替换为空字符串 - 对于有时可以设置为空字符串而不是不存在的环境变量很有用,您可以使用:

const N = f => (...a) => f(...a.map(v => (v === '' ? undefined : v)));

如果你想替换所有虚假值,你可以像这样使用它:

const N = f => (...a) => f(...a.map(v => (v || undefined)));

如果你想替换空 objects,你可以使用:

const N = f => (...a) => f(...a.map(v => (Object.keys(v).length ? v : undefined)));

等等...

结论

关键是这是您的代码,您知道函数的 API 应该是什么以及默认值应该如何工作。幸运的是 Java脚本足够强大,可以让您通过一些高阶函数魔术轻松实现您需要的功能(即使这不是默认值的默认行为,可以这么说)。

ramdajs 版本号

// import ramda

// infra code
const isEmptyOrNil = (value) => isEmpty(value) || isNil(value)
const nullChange = (defaultValue) => (value) => isEmptyOrNil(value)? defaultValue: value

// single null
const _somef = (value) => value
const somefWithDefault = (defaultValue) => pipe(nullChange(defaultValue), _somef)
const somef = somefWithDefault('myValue')

somef(null)

// many args
const _somef2 = (value, value2, value3) => value + value2 + value3
const nullChangeMyValue = nullChange('myValue')
const somef2 = useWith(_somef2, [nullChangeMyValue, nullChangeMyValue, nullChangeMyValue])

somef2(null, undefined, 1234)

// many args2 version2
const nullChangeWith = (defaultValue) => nullChange(defaultValue)
const arrayNullChangeWith = (defaultValue) => (array) => array.map(nullChangeWith(defaultValue))
const arg2Array = (...args) => args
const funcApplyDefaultValue = (defaultValue) => (func) => pipe(
  arg2Array, arrayNullChangeWith(defaultValue), apply(func)
)

const v2somef2 = funcApplyDefaultValue(8)(_somef2)

v2somef2(1,2,null)

gits: https://gist.github.com/otwm/3a6358e53ca794cc2e57ade4af91d3bb