在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: ...});
其中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
(这不同于没有任何值)。 这意味着你不能使用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对象?
所以…“传递对象”技巧对我来说不起作用……实际上,在我知道函数期望的参数之前,必须查看每个函数的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的情况下,调用者必须将a
和b
作为常规parameter passing。 但是,如果您执行提供的代码作为初始解决scheme:
func = bob.fn.namedArgFunction(func);
然后, 你可以传递一个对象作为参数 ,而不是a
和b
:
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