Reputation: 20379
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 dat
in 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
Reputation: 43196
How does
JavaScript
"know" thatdat
in the first case is the data (and not theselector
)?
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
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:
arguments
array-like object which is created for each function and contains the arguments and a length
property saying how many there are.undefined
.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
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
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