How does javascript resolve parameters?

Looking at the jQuery source code for on, types are checked by hand and any omitted parameters are accounted for manually (parameters always populate left-to-right):

function on( elem, types, selector, data, fn, one ) {
    var origFn, type;

    // Types can be a map of types/handlers
    if ( typeof types === "object" ) {

        // ( types-Object, selector, data )
        if ( typeof selector !== "string" ) {

            // ( types-Object, data )
            data = data || selector;
            selector = undefined;
        }
        for ( type in types ) {
            on( elem, type, selector, data, types[ type ], one );
        }
        return elem;
    }

    if ( data == null && fn == null ) {

        // ( types, fn )
        fn = selector;
        data = selector = undefined;
    } else if ( fn == null ) {
        if ( typeof selector === "string" ) {

...etc.

This might be shocking coming from a non-dynamically typed background, but it's pretty commonplace in JS.


How doe JavaScript "know" that datin the first case is the data (and not the selector)?

It doesn't. The language just passes the arguments given by the caller to the function. Figuring out what the caller passed is handled by the function, not the language.

Apparently there is no positional matching of neither of the two signatures?

JavaScript only uses positional matching. As of ES2015+, it will also provide defaults for missing parameters (or parameters whose passed argument is the value undefined).

So how does that work internally and how could I use this pattern for my own functions?

In JavaScript, you don't have to pass the same number of arguments to a function as the number of parameters it declares; you can pass fewer or more. The function can tell what you've passed it in three ways:

  • By looking at the special arguments array-like object which is created for each function and contains the arguments and a length property saying how many there are.
  • By looking at the values of the parameters and/or their types. If the caller hasn't provided an argument for a parameter, the parameter's value will be the value undefined.
  • In ES2015+, by using a rest parameter which gathers up all the provided arguments at that position and afterward into a true array.

Here's an example:

function example(obj, number, str) {
    console.log("Arguments received: " + arguments.length);
    if (typeof number === "string") {
        // Caller provided a string, not anumber, as the second
        // argument; move things around
        str = number;
        number = undefined;
    }
    console.log("obj", obj);
    console.log("number", number);
    console.log("str", str);
}
console.log('Calling example({}, 42, "forty-two");');
example({}, 42, "forty-two");
console.log('Calling example({}, "no number given");');
example({}, "no number given");
.as-console-wrapper {
    max-height: 100% !important;£
}

Here's an example of using a rest parameter:

function example(obj, ...rest) {
    let number, str;
    if (rest.length === 2) {
        [number, str] = rest; // This is called "destructuring assignment"
    } else {
        [str] = rest;
    }
    if (typeof number === "string") {
        // Caller provided a string, not anumber, as the second
        // argument; move things around
        str = number;
        number = undefined;
    }
    console.log("obj", obj);
    console.log("number", number);
    console.log("str", str);
}
console.log('Calling example({}, 42, "forty-two");');
example({}, 42, "forty-two");
console.log('Calling example({}, "no number given");');
example({}, "no number given");
.as-console-wrapper {
    max-height: 100% !important;£
}


How does JavaScript "know" that dat in the first case is the data (and not the selector)?

It doesn't.

In fact functions in javascript do not need to declare their parameters explicitly. Each function has access to an object called arguments which holds (as you may have guessed) all the arguments passed to the function. So the function can just look at this object to find out all the parameters passed to it.

function foo() {
    console.log(arguments);
}

foo(1, 2, 3, 4);

So how does that work internally and how could I use this pattern for my own functions?

I believe I already explained how it works, but my advice is Don't use this pattern for your own functions. If you can, I'd even advice not to write plain javascript. There are many supersets of the language available which allows you to be more explicit with the types and parameters accepted by your function. Typescript is one such language that introduces the much needed type safety when writing javascript. You can also try scala.js if you are into that sort of thing.

Tags:

Javascript