thothal
thothal

Reputation: 20379

How does javascript resolve parameters?

I am fairly new to JavaScript and coming from a different programming background I have a tough time to understand how JavaScript knows how to resolve parameters passed to a function.

Let's look at and easy example. from the documentation of jQuery .on, I see 2 different signatures:

.on( events [, selector ] [, data ], handler )

.on( events [, selector ] [, data ] )

However, the following snippet produces perfectly valid code and does what is intended:

$(function() {
  let dat = {a: 1, b: 2}
  $('#do').on('click', dat, function(evt) {
    alert(evt.data.b);
  });
  $('#do2').on('click', function(evt) {
    alert("click");
  });
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="do">
  Click!
</button>
<button id="do2">
  Click!
</button>

How does JavaScript "know" that datin the first case is the data (and not the selector)? Apparently there is no positional matching of neither of the two signatures? So how does that work internally and how could I use this pattern for my own functions?

Upvotes: 8

Views: 880

Answers (4)

smac89
smac89

Reputation: 43196

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.

Upvotes: 4

T.J. Crowder
T.J. Crowder

Reputation: 1075039

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;£
}

Upvotes: 4

ggorlen
ggorlen

Reputation: 57155

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.

Upvotes: 5

Amadan
Amadan

Reputation: 198436

selector has to be a string. dat has to be an object. handler has to be a function. There is nothing in JavaScript that can do this for you: all of these checks are done explicitly in jQuery code.

Upvotes: 2

Related Questions