提问者:小点点

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


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

calculateBMI(70, height: 175);

如果我想在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
}

我已经用过的方法。还有别的办法吗?

我可以用任何图书馆来做这个。


共3个答案

匿名用户

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...
}

ES5

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

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

这样,函数调用就可以看起来像

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

其中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);
        };
    };
}());

您可以将其用作:

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 

演示

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

  • 如果最后一个参数是对象,则将其视为“命名参数对象”
  • 您将始终获得与在函数中定义的参数一样多的参数,但其中一些参数的值可能为undefined(这与根本没有值不同)。这意味着您不能使用arguments.length来测试传递了多少个参数。

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

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

或者甚至扩展function.prototype,以便您可以执行以下操作:

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

匿名用户

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

匿名用户

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

很多人说,只要使用“传递一个对象”技巧,就可以命名参数。

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

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

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

使用“传递对象”技巧的最大问题是,当您调用函数时,IDE会给您一个类型提示,仅此而已。。。我们如何知道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');

这样,我两全其美。。。新代码很容易编写,因为我的IDE给了我所有适当的参数提示。。。而且,在稍后维护代码时,我不仅可以一眼看到传递给函数的值,还可以看到参数的名称。我看到的唯一开销是将参数名称声明为局部变量,以避免污染全局名称空间。当然,这是一些额外的键入,但与在编写新代码或维护现有代码时查找文档块所花费的时间相比,这是微不足道的。