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

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

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


当前回答

NB。正如评论中提到的,我2016年的答案是不正确的,具有误导性。

尝试Node-6.4.0 (process.versions. Node-6.4.0)。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

但是必须遵守秩序!!这是标准行为吗?

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

其他回答

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

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

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

我们可以使用一个对象(几乎与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存储库。

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

尽管在实践中,我不认为这在您已经使用的技术之外提供了任何功能价值,但它在语义上更简洁一些。它仍然需要传递一个对象引用或一个对象字面量。

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));

这确实是伪代码,但我相信它会起作用(我知道它在TypeScript中起作用;我将它用于JavaScript)。

// Target Function
const myFunc = (a=1,b=2,c=3) => {a+b+c}

// Goal usage:
myFunc(a=5, b=6) // 14
myFunc(c=0) // 3

// Set your defaults
const myFuncDefaults = {a:1, b:2, c:3};

// Override them with passed parameters
const myFuncParams = (params) => { return Object.assign(myFuncDefaults, params)}


// Use the overloaded dict as the input
const myFunc2 = (params) => {
  let {a, b, c} = myFuncParams(params);
  return myFunc(a, b, c)
}

// Usage:
myFunc({a:5, b:6}) // 14
myFunc({c:0}) // 3

// Written more succinctly:
const myFunc = (params) => {
  let {a,b,c} = Object.assign({a:1, b:2, c:3}, params)
  return a + b + c
}

不管它的价值是什么,TypeScript通过提示来实现这一点:

interface IParams {
  a: number;
  b: number;
  c: number;
}

const myFunc = (params: Partial<IParams>): number => {
  const default: IParams = {a:1, b:2, c:3};
  let {a, b, c} = Object.assign(default, params)
  return a + b + c
}

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