'Mixed named and unnamed parameters in JavaScript?

For a function with an intuitive first argument (e.g. a find semantic) I'd like to provide a way to write a function call without argument names while more complicated cases should better be done using named arguments

/// simple intuitive approach
const result1 = object.find("hello");

/// bad: nobody knows what true stands for
const result1 = object.find("hello", true, true);

/// verbose but readable approach
const result2 = object.find({
    expression: "hello",
    case_sensitive: true,
    whole_word: true});

in Python the function signature would look like this:

def find(self, expression, *, case_sensitive=False, whole_word=False):
    return do_actual_search(expression, case_sensitive, whole_word);

I found some examples written in the following way:

find(args) {
    const [expression, case_sensitive, whole_word] = ((typeof args=== "object")
          ? [args["expression"],
             args["case_sensitive"] || false,
             args["whole_word"] || false]
          : [args, false, false]);
    return do_actual_search(expression, case_sensitive, whole_word);
}

Here you can write both - arguments with and without names (while in this case you would be forced to use 'named arguments' if you provide more than the expression)

In the provided example has only one 'intuitive' argument, so you probably could rename args to expression and it could still be considered OK to provide an associative array instead of just the expression:

find(expression) {
    const [exp, case_sensitive, whole_word] = ((typeof expression=== "object")
          ? [expression["expression"],
             expression["case_sensitive"] || false,
             expression["whole_word"] || false]
          : [expression, false, false]);
    return do_actual_search(exp, case_sensitive, whole_word);
}

Either way is hard to write documentation for since the first argument can be both - the whole set of arguments or just one specific argument.

It gets more complicated when you have more than one 'intuitive' arguments:

people.find('Mickey', 'Mouse');

or

people.find({
    firstName: 'Mickey',
    lastName: 'Mouse',
    fictive: true,
    born: "1928.11.18"
});

Are there any typical approaches for this when you try to provide a comfortable API or do I have to stick to just one of both approaches?



Solution 1:[1]

The problem of the approach you proposed is that, apart from the search term and the case_sensitive and the whole_word predicates, it is hard to know how to handle easily additional arguments.

If I were in your shoes, the function find would receive an object as argument and handle it via object destructuring in this way:

find({ expression, case_sensitive=false, whole_word=true, ...otherProps } ) {
    return do_actual_search(
         Object.assign({}, { expression, case_sensitive, whole_word }, otherProps)
    );
}

The function receives an object that gives default values to the properties case_sensitive and whole_word, so the user may provide only the search expression (feel free to change the default values if you need it).

The rest operator in otherProps will group any additional properties into an object, as explained in the MDN Spread syntax documentation:

The Rest/Spread Properties for ECMAScript proposal (stage 4) adds spread properties to object literals. It copies own enumerable properties from a provided object onto a new object.

Lastly, Object.assign will copy the properties of all the given objects ({ expression, case_sensitive, whole_word } and otherProps) into a new object that will contain the properties to perform the search.

What happens if no additional property is provided to the function find?

In this case, ...otherProps will be interpreted as an empty object.

According to Javascript for impatient programmers, by Alex Rauschmeyer:

All values are spreadable, even undefined and null

> {...undefined}

{}

> {...null}

{}

> {...123}

{}

> {...'abc'}

{ '0': 'a', '1': 'b', '2': 'c' }

> {...['a', 'b']}

{ '0': 'a', '1': 'b' }

Thus, if otherProps cannot be used to produce a key-value pair (that is, if it is undefined, null or a number), an empty object will be saved under the parameter name otherProps.

Implementation of spread/rest syntax to object literals

The proposal was published in 2018 as part of the Ecmascript specification and is already implemented in

  • Firefox 55,
  • Chrome 60,
  • Opera 47,
  • Safari 11.1,
  • Node 8.3.0.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1