允许在 JavaScript 中使用命名参数或位置参数

8

如何让函数既能接收命名参数(foo({a: 'hello', b: 'it is me'})),又能接收位置参数(foo('hello', 'it is me'))?

我知道可以通过向函数传递一个对象来模拟命名参数:

function foo(options) {
    options = options || {};
    var a = options.a || 'peanut'; // whatever default value
    var b = options.b || 'butter'; // whatever default value
    console.log(a, b);
}

// ES6 allows automatic destructuring
function foo({a = 'peanut', b = 'butter'} = {}) {
    console.log(a, b);
}

但这不允许我传递位置参数。

我想使用ES6,但ES5的任何东西也可以。


你可以检测出两种情况之间的差异,但前提是你需要了解参数的预期类型,并且能够测试以确定传递给函数的是哪种情况。 - jfriend00
@jfriend00 考虑检查第一个参数是否为一个对象,以确定是否使用了命名参数。但是,如果我需要a也是一个对象,那将会混淆两种情况。有什么想法吗? - AKG
2
JavaScript检测不同变量传递方案的技术取决于参数的类型和数量。你必须设计一种方案,让你能够区分不同的可能性。你可以查看如何在Javascript中重载函数来了解更多可能性的详细讨论。 - jfriend00
太多的糖分。使用明确的输入编写良好的函数。仅仅因为某些特定的编程语言可以实现某些功能,并不意味着你就应该这样做。 - Mulan
3个回答

4

首先,我真的建议您坚持一种方法。就像您所说的,使用 "named" 或 "anonymous" 都可以,但不要混合使用。

function foo({a = 'peanut', b = 'butter'} = {}) {
    console.log(a, b);
}

位置参数:

function foo(a = 'peanut', b = 'butter') {
    console.log(a, b);
}

请选择适合您功能的一个,不要混用
如果您确实由于某种原因需要两个,您可以使用标准重载技术。只有在您第一个位置参数不是对象时,它才能正常工作。我建议使用以下其中一种习惯用法之一:
function foo(a, b) { // positional is normal case
    if (arguments.length == 1 && typeof arguments[0] == "object")
        {a, b} = arguments[0];

    console.log(a, b);
}

function foo({a, b}) { // named is normal case
    if (arguments.length > 1 || typeof arguments[0] != "object")
        [a, b] = arguments;

    console.log(a, b);
}

如果你需要默认值,无论如何都会变得很丑陋:

function foo(a, b) {
    var opts = (arguments.length == 1 && typeof arguments[0] == "object")
      ? arguments[0]
      : {a, b};
    ({a = 'peanut', b = 'butter'} = opts);

    console.log(a, b);
}

1
我想这样的代码可以起作用:

function foo(...options){
   if (typeof options[0] === 'object'){
    console.log('expect object', options[0]);
  }else{
    console.log('expect array', options);  
  }
}

foo('peanut', 'butter');
foo({a:'peanut', b:'butter'});

这很有道理。但是,如果我执行 foo('hello'),那么 options.length === 1。然后,如果我要检查 options[0] 是否为对象,那么 foo({'whatever': 'object'}) 也会失败。还有其他的想法吗? - AKG
如果第一个参数是对象,可以使用typeof来检查其类型。真实的,您可以检查第一个参数的类型。 - glued
你说得没错,但是如果我本来就期望 a 是一个对象呢?那么我会传递一个对象给 a,但这样它就会被错误地识别为命名参数集。 - AKG
1
此外,typeof options[0] === 'object' 会引发一系列问题:例如 typeof null === 'object' - AKG
@AKG 由于你的参数要求,typeof new String() === "object" 更糟糕。 - CodingIntrigue

1
我认为没有内置的东西可以做到这一点,但是这段代码应该适用于你的情况。
function foo({a = 'peanut', b = 'butter'} = {}) {
    if (typeof arguments[0] === 'string') {
        return foo({a: arguments[0], b: arguments[1]})
    }
    console.log(a, b);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接