Is there a way to provide named parameters in a function call in JavaScript?

前端 未结 10 1715
抹茶落季
抹茶落季 2020-11-22 07:28

I find the named parameters feature in C# quite useful in some cases.

calculateBMI(70, height: 175);

What can I use if I want this in JavaS

相关标签:
10条回答
  • 2020-11-22 07:42

    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
    
    0 讨论(0)
  • 2020-11-22 07:45

    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.

    0 讨论(0)
  • 2020-11-22 07:53

    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: ...});
    
    0 讨论(0)
  • 2020-11-22 07:57

    Contrary to what is commonly believed, named parameters can be implemented in standard, old-school JavaScript (for boolean parameters only) by means of a simple, neat coding convention, as shown below.

    function f(p1=true, p2=false) {
        ...
    }
    
    f(!!"p1"==false, !!"p2"==true); // call f(p1=false, p2=true)
    

    Caveats:

    • Ordering of arguments must be preserved - but the pattern is still useful, since it makes it obvious which actual argument is meant for which formal parameter without having to grep for the function signature or use an IDE.

    • This only works for booleans. However, I'm sure a similar pattern could be developed for other types using JavaScript's unique type coercion semantics.

    0 讨论(0)
提交回复
热议问题