可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I find the named parameters feature in C# quite useful in some cases.
calculateBMI(70, height: 175);
What if I want this in javascript?
What I don\'t want is -
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
}
That approach I\'ve already used. Is there another way?
I\'m okay using any library do this. (Or somebody can point me to one that already does)
回答1:
ES2015 and later
In ES2015, parameter destructuring can be used to simulate named parameters. It would require the caller to pass an object, but you can avoid all of the checks inside the function if you also use default parameters:
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
There is a way to come close to what you want, but it is based on the output of Function.prototype.toString
[ES5], which is implementation dependent to some degree, so it might not be cross-browser compatible.
The idea is to parse the parameter names from the string representation of the function so that you can associate the properties of an object with the corresponding parameter.
A function call could then look like
func(a, b, {someArg: ..., someOtherArg: ...});
where a
and b
are positional arguments and the last argument is an object with named arguments.
For example:
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);
};
};
}());
Which you would use as:
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
There are some drawbacks to this approach (you have been warned!):
- If the last argument is an object, it is treated as a \"named argument objects\"
- You will always get as many arguments as you defined in the function, but some of them might have the value
undefined
(that\'s different from having no value at all). That means you cannot use arguments.length
to test how many arguments have been passed.
Instead of having a function creating the wrapper, you could also have a function which accepts a function and various values as arguments, such as
call(func, a, b, {posArg: ... });
or even extend Function.prototype
so that you could do:
foo.execute(a, b, {posArg: ...});
回答2:
No - the object approach is JavaScript\'s answer to this. There is no problem with this provided your function expects an object rather than separate params.
回答3:
This issue has been a pet peeve of mine for some time. I am a seasoned programmer with many languages under my belt. One of my favorite languages that I have had the pleasure to use is Python. Python supports named parameters without any trickery.... Since I started using Python (some time ago) everything became easier. I believe that every language should support named parameters, but that just isn\'t the case.
Lot\'s of people say to just use the \"Pass an object\" trick so that you have named parameters.
/**
* My Function
*
* @param {Object} arg1 Named arguments
*/
function myFunc(arg1) { }
myFunc({ param1 : 70, param2 : 175});
And that works great, except..... when it comes to most IDEs out there, a lot of us developers rely on type / argument hints within our IDE. I personally use PHP Storm (Along with other JetBrains IDEs like PyCharm for python and AppCode for Objective C)
And the biggest problem with using the \"Pass an object\" trick is that when you are calling the function, the IDE gives you a single type hint and that\'s it... How are we supposed to know what parameters and types should go into the arg1 object?
So... the \"Pass an object\" trick doesn\'t work for me... It actually causes more headaches with having to look at each function\'s docblock before I know what parameters the function expects.... Sure, it\'s great for when you are maintaining existing code, but it\'s horrible for writing new code.
Well, this is the technique I use.... Now, there may be some issues with it, and some developers may tell me I\'m doing it wrong, and I have an open mind when it comes to these things... I am always willing to look at better ways of accomplishing a task... So, if there is an issue with this technique, then comments are welcome.
/**
* My Function
*
* @param {string} arg1 Argument 1
* @param {string} arg2 Argument 2
*/
function myFunc(arg1, arg2) { }
var arg1, arg2;
myFunc(arg1=\'Param1\', arg2=\'Param2\');
This way, I have the best of both worlds... new code is easy to write as my IDE gives me all the proper argument hints... And, while maintaining code later on, I can see at a glance, not only the value passed to the function, but also the name of the argument. The only overhead I see is declaring your argument names as local variables to keep from polluting the global namespace. Sure, it\'s a bit of extra typing, but trivial compared to the time it takes to look up docblocks while writing new code or maintaining existing code.
回答4:
If you want to make it clear what each of the parameters are, rather than just calling
someFunction(70, 115);
why not do the following
var width = 70, height = 115;
someFunction(width, height);
sure, it\'s an extra line of code, but it wins on readability.
回答5:
Another way would be to use attributes of a suitable object, e.g. like so:
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);
Of course for this made up example it is somewhat meaningless. But in cases where the function looks like
do_something(some_connection_handle, some_context_parameter, some_value)
it might be more useful. It also could be combined with \"parameterfy\" idea to create such an object out of an existing function in a generic way. That is for each parameter it would create a member that can evaluate to a partial evaluated version of the function.
This idea is of course related to Schönfinkeling aka Currying.
回答6:
There is another way. If you\'re passing an object by reference, that object\'s properties will appear in the function\'s local scope. I know this works for Safari (haven\'t checked other browsers) and I don\'t know if this feature has a name, but the below example illustrates its use.
Although in practice I don\'t think that this offers any functional value beyond the technique you\'re already using, it\'s a little cleaner semantically. And it still requires passing a object reference or an object literal.
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));
回答7:
Trying Node-6.4.0 ( process.versions.v8 = \'5.0.71.60\') and Node Chakracore-v7.0.0-pre8 and then Chrome-52 (V8=5.2.361.49), I\'ve noticed that named parameters are almost implemented, but that order has still precedence. I can\'t find what the ECMA standard says.
>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
But order is required!! Is it the standard behaviour?
> f(b=10)
a=10 + b=2 = 12