我发现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
}

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

我可以使用任何库来做到这一点。


当前回答

是的,嗯,有点。我找到了两个解决方案。我只解释一个。

在这个解决方案中,我们放弃了位置参数。

我们可以使用一个对象(几乎与Python中的dict相同)来传递参数。

在这个例子中,我使用函数来生成一个图像文件的名称:

// First we define our function with just ONE argument
function name_of_img(img_desc){

  // With this step, any undefined value will be assigned a value
  if(img_desc.size == undefined) {img_desc.size = "400x500"}
  if(img_desc.format == undefined) {img_desc.format = ".png"}

  console.log(img_desc.size + img_desc.format)
}

// Notice inside our function we're passing a dict/object

name_of_img({size: "200x250", format : ".jpg"})
// In Python name_of_img(size="200x250" , format="jpg")
// returns "200x250.jpg"

name_of_img({size: "1200x950"})
// In Python name_of_img(size="1200x950")
// returns "1200x950.png"

我们可以修改这个例子,所以我们也可以使用位置参数,我们也可以修改它,所以非有效参数可以传递,我想我会做一个关于这个的GitHub存储库。

其他回答

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

someFunction(70, 115);

做以下几点:

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

当然,这是额外的一行代码,但它在可读性上更胜一筹。

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

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

不——对象方法是JavaScript对此的答案。如果你的函数需要一个对象而不是单独的参数,这就没有问题。

调用函数f,将命名参数作为对象传递

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

基本上是调用它的组合f(…g(o)),其中我使用蔓延语法和g是一个“绑定”映射连接对象值及其参数位置。

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

// 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}))

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

例如,在函数的定义中,你可能想要将高度和宽度缩写为h和w,使其更简短和清晰,同时你仍然想要使用全名来调用它:

// 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})

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

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: ...});

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

例如:

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 

DEMO

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

如果最后一个参数是一个对象,它将被视为“命名参数对象” 您将始终获得与在函数中定义的相同数量的参数,但其中一些参数的值可能为undefined(这与完全没有值不同)。这意味着你不能使用参数。长度来测试传递了多少个参数。


与其让函数创建包装器,还可以让函数接受函数和各种值作为参数,例如

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

甚至扩展函数。这样你就可以做:

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