Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does javascript resolve parameters?

Tags:

javascript

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?

like image 423
thothal Avatar asked Sep 13 '19 06:09

thothal


2 Answers

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.

like image 195
ggorlen Avatar answered Nov 05 '22 11:11

ggorlen


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;£
}
like image 4
T.J. Crowder Avatar answered Nov 05 '22 12:11

T.J. Crowder