在javascript中命名参数

Rob*_*ben 176 javascript function optional-parameters named-parameters

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

calculateBMI(70, height: 175);
Run Code Online (Sandbox Code Playgroud)

如果我想在javascript中使用该怎么办?


我不想要的是 -

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
}
Run Code Online (Sandbox Code Playgroud)

我已经使用过这种方法.还有另外一种方法吗?

我可以使用任何库来做这件事.(或者有人可以指出我已经做过的那个)

Fel*_*ing 168

ES2015及更高版本

在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...
}
Run Code Online (Sandbox Code Playgroud)

ES5

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

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

然后函数调用可能看起来像

func(a, b, {someArg: ..., someOtherArg: ...});
Run Code Online (Sandbox Code Playgroud)

where ab是位置参数,最后一个参数是带有命名参数的对象.

例如:

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);
        };
    };
}());
Run Code Online (Sandbox Code Playgroud)

您将使用哪个:

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 
Run Code Online (Sandbox Code Playgroud)

DEMO

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

  • 如果最后一个参数是一个对象,则将其视为"命名参数对象"
  • 您将始终获得与函数中定义的参数一样多的参数,但其中一些参数可能具有该值undefined(这与完全没有值的情况不同).这意味着您不能用于arguments.length测试已传递的参数数量.

您可以使用一个函数来接受函数和各种值作为参数,而不是创建包装器的函数

call(func, a, b, {posArg: ... });
Run Code Online (Sandbox Code Playgroud)

甚至延伸,Function.prototype以便您可以:

foo.execute(a, b, {posArg: ...});
Run Code Online (Sandbox Code Playgroud)

  • @NobodyMan:是的.我在箭头函数之前写了这个答案.在ES6中,我实际上会求助于参数解构. (3认同)

Utk*_*nos 62

- 对象方法是JavaScript的答案.如果你的函数需要一个对象而不是单独的params,那么这没有问题.

  • @RobertMaben - 问题的答案是,没有本地方法来收集声明的变量或函数,而不知道它们存在于特定的命名空间中.仅仅因为答案很短,它并没有否定它作为答案的适用性 - 你不同意吗?那里有很短的答案,沿着"不,不可能"的路线.他们可能是短暂的,但他们也是问题的答案. (33认同)
  • 现在有es6:http://www.2ality.com/2011/11/keyword-parameters.html (2认同)
  • 这绝对是一个公平的答案——“命名参数”是一种语言特性。在没有这样的特性的情况下,对象方法是下一个最好的选择。 (2认同)

Ray*_*rea 29

这个问题一直是我的一个宠儿.我是一位经验丰富的程序员,拥有多种语言.我有幸使用的一种我最喜欢的语言是Python.Python支持命名参数,没有任何诡计....自从我开始使用Python(前一段时间)后,一切都变得更加容易.我相信每种语言都应该支持命名参数,但事实并非如此.

很多人说只是使用"传递对象"技巧,以便你有命名参数.

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

myFunc({ param1 : 70, param2 : 175});
Run Code Online (Sandbox Code Playgroud)

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

使用"传递对象"技巧的最大问题是,当您调用该函数时,IDE会为您提供单一类型提示,就是这样......我们应该知道哪些参数和类型应该进入arg1对象?

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

所以..."传递一个对象"技巧对我来说不起作用......在我知道函数所期望的参数之前,它实际上会导致更多的麻烦,不得不查看每个函数的docblock ....当然,这对于当您维护现有代码时,但编写新代码非常糟糕.

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

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

var arg1, arg2;
myFunc(arg1='Param1', arg2='Param2');
Run Code Online (Sandbox Code Playgroud)

这样,我充分利用了两个世界......新代码很容易编写,因为我的IDE给了我所有正确的参数提示......而且,在稍后维护代码的同时,我可以一目了然地看到,不仅仅是传递给函数的值,也是参数的名称.我看到的唯一开销是将您的参数名称声明为局部变量,以防止污染全局命名空间.当然,这是一个额外的打字,但与编写新代码或维护现有代码时查找docblock所花费的时间相比微不足道.

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

  • 似乎这只是在一些未来的维护者出现并且认为他们可以改变参数顺序(但显然不能)时遇到麻烦. (11认同)
  • 提出的模式与命名参数无关,顺序仍然很重要,名称不需要与实际参数保持同步,命名空间被不必要的变量污染,并且暗示不存在的关系。 (6认同)
  • 我认为`myFunc(/*arg1*/'Param1',/*arg2*/'Param2');`比`myFunc(arg1 ='Param1',arg2 ='Param2')更好;`,因为没有机会欺骗读者,而不是任何实际的命名参数正在发生 (5认同)
  • 抱歉,但这不值得称为“技术”,这不能回答问题,因为它*不*为您提供命名参数,它引入了不必要的变量,如果您只考虑 `myFunc(arg2= 'Param2', arg1='Param1');` 或 `myFunc(arg2='Param2');` 即可。这完全是一种反模式,而且是一个可怕的答案。请删除! (4认同)
  • 这种技术唯一的问题是你不能改变参数的顺序......不过我个人对此很好。 (3认同)

dav*_*v_i 20

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

someFunction(70, 115);
Run Code Online (Sandbox Code Playgroud)

为什么不做以下

var width = 70, height = 115;
someFunction(width, height);
Run Code Online (Sandbox Code Playgroud)

当然,这是一个额外的代码行,但它在可读性上获胜.

  • 它不仅涉及额外的代码行,还涉及参数的顺序并使它们成为可选的.所以你可以用命名参数来写这个:`someFunction(height:115);`但是如果你写'someFunction(height);`你实际上是在设置宽度. (17认同)
  • +1遵循KISS原则,加上它也有助于调试.但是,我认为每个var都应该在自己的行上,尽管性能略有下降([http://stackoverflow.com/questions/9672635/javascript-var-statement-and-performance](http://stackoverflow). COM /问题/ 9672635/JavaScript的VAR语句和性能)). (4认同)

Udo*_*ein 6

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

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);
Run Code Online (Sandbox Code Playgroud)

当然,对于这个组成的例子,它有点毫无意义.但是在函数看起来像的情况下

do_something(some_connection_handle, some_context_parameter, some_value)
Run Code Online (Sandbox Code Playgroud)

它可能更有用.它还可以与"parameterfy"思想相结合,以通用方式从现有函数中创建这样的对象.这是为每个参数创建一个可以评估函数的部分评估版本的成员.

这个想法当然与Schönfinkeling又名Currying有关.