我发现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存储库。

其他回答

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

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

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

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

还有另一种方法。如果通过引用传递对象,则该对象的属性将出现在函数的局部作用域中。我知道这适用于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));

调用函数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})

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