4

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?

5
  • Usually what you describe is done using objects as parameters like some of the examples you have. It's an easy way to provide "named" parameters as well as optional ones. JS doesn't have a way to name parameters any other way. Commented Jul 31, 2019 at 6:43
  • In JavaScript, you need to use : instead of = in object literals. Commented Jul 31, 2019 at 9:35
  • Use Typescript and define types for different parameters, you would have a clear signature. Commented Jul 31, 2019 at 9:51
  • I'll check it out some time but for now that doesn't solve my problem, does it? It would be easy to have a clear signature but all I want is optionally named parameters.. Commented Jul 31, 2019 at 11:19
  • 1
    TL;DR, like most other languages Javascript lacks named arguments, so anything you do will be an awkward workaround. Two common tips would be to use separate functions instead of variadic functions, and constants instead of magic booleans. The result could be something like find('hello'), findWholeWord('hello', CASE_SENSITIVE) and such. Commented Aug 5, 2019 at 8:48

1 Answer 1

2

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.
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.