在javascript中命名参数

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

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 } 

我已经使用了这种方法。 有另一种方法吗?

我可以使用任何图书馆做这个。 (或者有人可以指出我已经这样做了)

ES2015

在ES2015中, 参数解构可以用来模拟命名参数。 这将需要调用者传递一个对象,但是如果你还使用默认参数,你可以避免在函数内部进行所有的检查:

 myFunction({ param1 : 70, param2 : 175}); function myFunction({param1, param2}={}){ // ... } 

ES5

有一种方法可以接近你想要的,但是它是基于Function.prototype.toString [ES5]的输出,它在一定程度上依赖于实现,所以它可能不是跨浏览器兼容的。

这个想法是从函数的string表示中parsing参数名称,以便可以将对象的属性与相应的参数关联起来。

函数调用可能看起来像

 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 

DEMO

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

  • 如果最后一个参数是一个对象,则将其视为“命名参数对象”
  • 你将总是得到和你在函数中定义的一样多的参数,但是其中一些参数的值可能是undefined (这不同于没有任何值)。 这意味着你不能使用arguments.length来testing已经传递了多less个参数。

而不是有一个函数创build包装,你也可以有一个函数接受一个函数和各种值作为参数,如

 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中的types/参数提示。 我个人使用PHP Storm(与其他JetBrains IDE一样PyCharm for Python和AppCode for Objective C)

而使用“传递对象”技巧的最大问题是,当你调用函数时,IDE会给你一个单一的types提示,那就是它…我们应该怎样知道什么参数和types应该进入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'); 

这样,我就拥有了两全其美的function……新的代码很容易编写,因为我的IDE给了我所有适当的参数提示……而且,在保留代码的同时,我可以一眼就看出,传递给函数的值,也是参数的名字。 我看到唯一的开销是声明你的参数名称作为本地variables,以防止污染全局命名空间。 当然,这只是一些额外的input,与编写新代码或维护现有代码时查找docblock所花费的时间相比是微不足道的。

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

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

 someFunction(70, 115); 

为什么不做下面的事情

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

当然,这是一个额外的代码行,但它赢得了可读性。

bob.js (由我写的)可以将您的常规函数​​转换为接受命名参数的函数,在一行中:

 func = bob.fn.namedArgFunction(func); 

更多细节

我想有些读者可能需要澄清一下上面的解决scheme。 所以,这里是缺less的部分。

函数定义将需要定期的参数 (而不是对象),所以你将有一个清晰的,意图揭示的代码:

 var func = function(a, b) { return a + b; } 

在没有调用bob.js的情况下,调用者必须将ab作为常规parameter passing。 但是,如果您执行提供的代码作为初始解决scheme:

 func = bob.fn.namedArgFunction(func); 

然后, 你可以传递一个对象作为参数 ,而不是ab

 var arg = { a: 1, b: 2 }; var sum = func(arg); //sum is 3 (=1+2). 

注意 :这个解决scheme工作正常,没有缩小。 但是,如果你正在缩小你的JavaScript函数,那么函数参数名称将在缩小期间被改变,所以这个解决scheme将无济于事。 我想我可能需要实现一个模式类似于angular(他们有它dependency injection)明确提到的参数名称。 但是到目前为止,这个解决scheme(以及在这个线程中提供的其他一些解决scheme)不能适应缩小。

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

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

当然,对于这个例子来说,这是没有意义的。 但是在function看起来像的情况下

 do_something(some_connection_handle, some_context_parameter, some_value) 

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

这个想法当然与Schönfinkelingaka Currying有关。

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

尽pipe在实践中,我认为这不会提供超出您已经使用的技术的任何function价值,但它在语义上稍微更清晰。 它仍然需要传递一个对象引用或对象文字。

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

尝试Node-6.4.0(process.versions.v8 ='5.0.71.60')和Node Chakracore-v7.0.0-pre8,然后Chrome-52(V8 = 5.2.361.49),我注意到命名参数几乎但是这个顺序仍然是优先的。 我无法findECMA标准所说的内容。

 >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