有没有办法在 JavaScript 的函数调用中提供命名参数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11796093/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 07:07:48  来源:igfitidea点击:

Is there a way to provide named parameters in a function call in JavaScript?

javascriptfunctionoptional-parametersnamed-parameters

提问by Robin Maben

I find the named parameters feature in C# quite useful in some cases.

我发现 C# 中的命名参数功能在某些情况下非常有用。

calculateBMI(70, height: 175);

What can I use if I want this in JavaScript?

如果我想在 JavaScript 中使用它,我可以使用什么?



What I don't want is this:

我不想要的是这个:

myFunction({ param1: 70, param2: 175 });

function myFunction(params){
  // Check if params is an object
  // Check if the parameters I need are non-null
  // Blah blah
}

That approach I've already used. Is there another way?

我已经使用过这种方法。还有其他方法吗?

I'm okay using any library to do this.

我可以使用任何图书馆来做到这一点。

采纳答案by Felix Kling

ES2015 and later

ES2015 及更高版本

In ES2015, parameter destructuringcan be used to simulate named parameters. It would require the caller to pass an object, but you can avoid all of the checks inside the function if you also use default parameters:

在 ES2015 中,可以使用参数解构来模拟命名参数。它需要调用者传递一个对象,但如果您还使用默认参数,则可以避免函数内部的所有检查:

myFunction({ param1 : 70, param2 : 175});

function myFunction({param1, param2}={}){
  // ...function body...
}

// Or with defaults, 
function myFunc({
  name = 'Default user',
  age = 'N/A'
}={}) {
  // ...function body...
}


ES5

ES5

There is a way to come close to what you want, but it is based on the output of Function.prototype.toString[ES5], which is implementation dependent to some degree, so it might not be cross-browser compatible.

有一种方法可以接近您想要的结果,但它基于Function.prototype.toString[ES5]的输出,这在某种程度上取决于实现,因此它可能无法跨浏览器兼容。

The idea is to parse the parameter names from the string representation of the function so that you can associate the properties of an object with the corresponding parameter.

这个想法是从函数的字符串表示中解析参数名称,以便您可以将对象的属性与相应的参数相关联。

A function call could then look like

一个函数调用可能看起来像

func(a, b, {someArg: ..., someOtherArg: ...});

where aand bare positional arguments and the last argument is an object with named arguments.

其中ab是位置参数,最后一个参数是带有命名参数的对象。

For example:

例如:

var parameterfy = (function() {
    var pattern = /function[^(]*\(([^)]*)\)/;

    return function(func) {
        // fails horribly for parameterless functions ;)
        var args = func.toString().match(pattern)[1].split(/,\s*/);

        return function() {
            var named_params = arguments[arguments.length - 1];
            if (typeof named_params === 'object') {
                var params = [].slice.call(arguments, 0, -1);
                if (params.length < args.length) {
                    for (var i = params.length, l = args.length; i < l; i++) {
                        params.push(named_params[args[i]]);
                    }
                    return func.apply(this, params);
                }
            }
            return func.apply(null, arguments);
        };
    };
}());

Which you would use as:

您将用作:

var foo = parameterfy(function(a, b, c) {
    console.log('a is ' + a, ' | b is ' + b, ' | c is ' + c);     
});

foo(1, 2, 3); // a is 1  | b is 2  | c is 3
foo(1, {b:2, c:3}); // a is 1  | b is 2  | c is 3
foo(1, {c:3}); // a is 1  | b is undefined  | c is 3
foo({a: 1, c:3}); // a is 1  | b is undefined  | c is 3 

DEMO

演示

There are some drawbacksto this approach (you have been warned!):

这种方法有一些缺点(你已经被警告过!):

  • If the last argument is an object, it is treated as a "named argument objects"
  • You will always get as many arguments as you defined in the function, but some of them might have the value undefined(that's different from having no value at all). That means you cannot use arguments.lengthto test how many arguments have been passed.
  • 如果最后一个参数是一个对象,则将其视为“命名参数对象”
  • 您将始终获得与您在函数中定义的参数一样多的参数,但其中一些参数可能具有值undefined(这与根本没有值不同)。这意味着您不能使用arguments.length来测试已经传递了多少参数。


Instead of having a function creating the wrapper, you could also have a function which accepts a function and various values as arguments, such as

除了让函数创建包装器之外,您还可以拥有一个接受函数和各种值作为参数的函数,例如

call(func, a, b, {posArg: ... });

or even extend Function.prototypeso that you could do:

甚至扩展Function.prototype以便您可以执行以下操作:

foo.execute(a, b, {posArg: ...});

回答by Utkanos

No- the object approach is JavaScript's answer to this. There is no problem with this provided your function expects an object rather than separate params.

- 对象方法是 JavaScript 对此的回答。如果您的函数需要一个对象而不是单独的参数,则这没有问题。

回答by Ray Perea

This issue has been a pet peeve of mine for some time. I am a seasoned programmer with many languages under my belt. One of my favorite languages that I have had the pleasure to use is Python. Python supports named parameters without any trickery.... Since I started using Python (some time ago) everything became easier. I believe that every language should support named parameters, but that just isn't the case.

一段时间以来,这个问题一直是我的心头大患。我是一名经验丰富的程序员,掌握多种语言。我有幸使用过的最喜欢的语言之一是 Python。Python 支持命名参数,没有任何技巧......自从我开始使用 Python(一段时间前)以来,一切都变得更容易了。我相信每种语言都应该支持命名参数,但事实并非如此。

Lot's of people say to just use the "Pass an object" trick so that you have named parameters.

很多人说只需使用“传递对象”技巧即可命名参数。

/**
 * My Function
 *
 * @param {Object} arg1 Named arguments
 */
function myFunc(arg1) { }

myFunc({ param1 : 70, param2 : 175});

And that works great, except..... when it comes to most IDEs out there, a lot of us developers rely on type / argument hints within our IDE. I personally use PHP Storm (Along with other JetBrains IDEs like PyCharm for python and AppCode for Objective C)

这很好用,除了......当涉及到大多数 IDE 时,我们中的很多开发人员都依赖于我们 IDE 中的类型/参数提示。我个人使用 PHP Storm(以及其他 JetBrains IDE,如 Python 的 PyCharm 和 Objective C 的 AppCode)

And the biggest problem with using the "Pass an object" trick is that when you are calling the function, the IDE gives you a single type hint and that's it... How are we supposed to know what parameters and types should go into the arg1 object?

使用“传递对象”技巧的最大问题是,当您调用该函数时,IDE 会为您提供一个类型提示,仅此而已……我们如何知道应该将哪些参数和类型放入arg1 对象?

I have no idea what parameters should go in arg1

我不知道 arg1 中应该包含哪些参数

So... the "Pass an object" trick doesn't work for me... It actually causes more headaches with having to look at each function's docblock before I know what parameters the function expects.... Sure, it's great for when you are maintaining existing code, but it's horrible for writing new code.

所以......“传递一个对象”技巧对我不起作用......它实际上会导致更多的麻烦,因为在我知道函数期望的参数之前必须查看每个函数的文档块......当然,它非常适合当您维护现有代码时,但编写新代码很糟糕。

Well, this is the technique I use.... Now, there may be some issues with it, and some developers may tell me I'm doing it wrong, and I have an open mind when it comes to these things... I am always willing to look at better ways of accomplishing a task... So, if there is an issue with this technique, then comments are welcome.

嗯,这就是我使用的技术......现在,它可能存在一些问题,一些开发人员可能会告诉我我做错了,而我对这些事情持开放态度......我总是愿意寻找更好的方法来完成任务......所以,如果这个技术有问题,欢迎提出意见。

/**
 * My Function
 *
 * @param {string} arg1 Argument 1
 * @param {string} arg2 Argument 2
 */
function myFunc(arg1, arg2) { }

var arg1, arg2;
myFunc(arg1='Param1', arg2='Param2');

This way, I have the best of both worlds... new code is easy to write as my IDE gives me all the proper argument hints... And, while maintaining code later on, I can see at a glance, not only the value passed to the function, but also the name of the argument. The only overhead I see is declaring your argument names as local variables to keep from polluting the global namespace. Sure, it's a bit of extra typing, but trivial compared to the time it takes to look up docblocks while writing new code or maintaining existing code.

通过这种方式,我拥有两全其美的优势……新代码很容易编写,因为我的 IDE 为我提供了所有正确的参数提示……而且,在稍后维护代码的同时,我可以一目了然,不仅传递给函数的值,还有参数的名称。我看到的唯一开销是将参数名称声明为局部变量,以免污染全局命名空间。当然,这有点额外的输入,但与在编写新代码或维护现有代码时查找文档块所需的时间相比是微不足道的。

Now, I have all the parameters and types when creating new code

现在,我在创建新代码时拥有所有参数和类型

回答by dav_i

If you want to make it clear what each of the parameters are, rather than just calling

如果你想清楚每个参数是什么,而不是仅仅调用

someFunction(70, 115);

why not do the following

为什么不做以下事情

var width = 70, height = 115;
someFunction(width, height);

sure, it's an extra line of code, but it wins on readability.

当然,这是额外的一行代码,但它在可读性方面胜出。

回答by Udo Klein

Another way would be to use attributes of a suitable object, e.g. like so:

另一种方法是使用合适对象的属性,例如:

function plus(a,b) { return a+b; };

Plus = { a: function(x) { return { b: function(y) { return plus(x,y) }}}, 
         b: function(y) { return { a: function(x) { return plus(x,y) }}}};

sum = Plus.a(3).b(5);

Of course for this made up example it is somewhat meaningless. But in cases where the function looks like

当然,对于这个编造的例子,它有点没有意义。但是在函数看起来像的情况下

do_something(some_connection_handle, some_context_parameter, some_value)

it might be more useful. It also could be combined with "parameterfy" idea to create such an object out of an existing function in a generic way. That is for each parameter it would create a member that can evaluate to a partial evaluated version of the function.

它可能更有用。它还可以与“parameterfy”想法相结合,以通用方式从现有函数中创建这样一个对象。也就是说,对于每个参数,它将创建一个成员,该成员可以评估为函数的部分评估版本。

This idea is of course related to Sch?nfinkeling aka Currying.

这个想法当然与 Sch?nfinkeling aka Currying 有关。

回答by Allen Ellison

There is another way. If you're passing an object by reference, that object's properties will appear in the function's local scope. I know this works for Safari (haven't checked other browsers) and I don't know if this feature has a name, but the below example illustrates its use.

还有另一种方法。如果您通过引用传递对象,则该对象的属性将出现在函数的局部作用域中。我知道这适用于 Safari(尚未检查其他浏览器),我不知道此功能是否有名称,但下面的示例说明了它的用法。

Although in practice I don't think that this offers any functional value beyond the technique you're already using, it's a little cleaner semantically. And it still requires passing a object reference or an object literal.

虽然在实践中我不认为这提供了超出您已经使用的技术的任何功能价值,但它在语义上更清晰一些。它仍然需要传递对象引用或对象文字。

function sum({ a:a, b:b}) {
    console.log(a+'+'+b);
    if(a==undefined) a=0;
    if(b==undefined) b=0;
    return (a+b);
}

// will work (returns 9 and 3 respectively)
console.log(sum({a:4,b:5}));
console.log(sum({a:3}));

// will not work (returns 0)
console.log(sum(4,5));
console.log(sum(4));

回答by Dmitri Zaitsev

Calling function fwith named parameters passed as the object

f使用作为对象传递的命名参数调用函数

o = {height: 1, width: 5, ...}

is basically calling its composition f(...g(o))where I am using the spread syntax and gis a "binding" map connecting the object values with their parameter positions.

基本上是f(...g(o))在我使用扩展语法的地方调用它的组合,并且g是一个“绑定”映射,将对象值与其参数位置连接起来。

The binding map is precisely the missing ingredient, that can be represented by the array of its keys:

绑定映射正是缺失的成分,可以用它的键数组表示:

// map 'height' to the first and 'width' to the second param
binding = ['height', 'width']

// take binding and arg object and return aray of args
withNamed = (bnd, o) => bnd.map(param => o[param])

// call f with named args via binding
f(...withNamed(binding, {hight: 1, width: 5}))

Note the three decoupled ingredients:the function, the object with named arguments and the binding. This decoupling allows for a lot of flexibility to use this construct, where the binding can be arbitrarily customized in function's definition and arbitrarily extended at the function call time.

注意三个解耦的成分:函数、带有命名参数的对象和绑定。这种解耦为使用此构造提供了很大的灵活性,其中绑定可以在函数定义中任意定制,并在函数调用时任意扩展。

For instance, you may want to abbreviate heightand widthas hand winside your function's definition, to make it shorter and cleaner, while you still want to call it with full names for clarity:

例如,您可能希望在函数定义中缩写heightand widthashw,以使其更短和更清晰,同时为了清楚起见,您仍然希望使用全名来调用它:

// use short params
f = (h, w) => ...

// modify f to be called with named args
ff = o => f(...withNamed(['height', 'width'], o))

// now call with real more descriptive names
ff({height: 1, width: 5})

This flexibility is also more useful for functional programming, where functions can be arbitrarily transformed with their original param names getting lost.

这种灵活性对于函数式编程也更有用,在函数式编程中,函数可以在丢失原始参数名称的情况下任意转换。

回答by jgran

Trying Node-6.4.0 ( process.versions.v8 = '5.0.71.60') and Node Chakracore-v7.0.0-pre8 and then Chrome-52 (V8=5.2.361.49), I've noticed that named parameters are almostimplemented, but that order has still precedence. I can't find what the ECMA standard says.

尝试 Node-6.4.0 ( process.versions.v8 = '5.0.71.60') 和 Node Chakracore-v7.0.0-pre8 然后 Chrome-52 (V8=5.2.361.49),我注意到命名参数几乎已实施,但该顺序仍具有优先权。我找不到 ECMA 标准所说的内容。

>function f(a=1, b=2){ console.log(`a=${a} + b=${b} = ${a+b}`) }

> f()
a=1 + b=2 = 3
> f(a=5)
a=5 + b=2 = 7
> f(a=7, b=10)
a=7 + b=10 = 17

But order is required!! Is it the standard behaviour?

但是需要下单!!这是标准行为吗?

> f(b=10)
a=10 + b=2 = 12

回答by Vince Spicer

Coming from Python this bugged me. I wrote a simple wrapper/Proxy for node that will accept both positional and keyword objects.

来自 Python 这让我很烦恼。我为 node 编写了一个简单的包装器/代理,它将接受位置和关键字对象。

https://github.com/vinces1979/node-def/blob/master/README.md

https://github.com/vinces1979/node-def/blob/master/README.md

回答by user234461

Contrary to what is commonly believed, named parameters can be implemented in standard, old-school JavaScript (for boolean parameters only) by means of a simple, neat coding convention, as shown below.

与人们普遍认为的相反,命名参数可以通过简单、整洁的编码约定在标准的老式 JavaScript(仅适用于布尔参数)中实现,如下所示。

function f(p1=true, p2=false) {
    ...
}

f(!!"p1"==false, !!"p2"==true); // call f(p1=false, p2=true)

Caveats:

注意事项:

  • Ordering of arguments must be preserved - but the pattern is still useful, since it makes it obvious which actual argument is meant for which formal parameter without having to grep for the function signature or use an IDE.

  • This only works for booleans. However, I'm sure a similar pattern could be developed for other types using JavaScript's unique type coercion semantics.

  • 必须保留参数的顺序 - 但该模式仍然有用,因为它可以清楚地表明哪个实际参数用于哪个形式参数,而无需 grep 函数签名或使用 IDE。

  • 这仅适用于布尔值。但是,我确信可以使用 JavaScript 独特的类型强制语义为其他类型开发类似的模式。