Javascript/JQuery arguments by type - javascript

I'm not quite sure if the title is correct because I'm not sure how to describe my question, but basically I'm wondering how jQuery can handle functions that take things like ("Some String", true, function(){}) and ("Some String", function() {}). IE, it seems like it's an overloaded function, I'd expect the function to be something like
function DoSomething(theStr, theBool, theFunc) {
//...
}
but that doesn't explain how it can handle the 2 argument call, at least to me. Anyone able to explain, or is it just a lot of if/else.

jQuery does type checking or arguments internally, shifting parameters (like a callback, typically at the end) forward if there are no arguments in-between. Let's take for example $.get() where the data argument is optional, the signature looks like this:
jQuery.get(url, [data], [callback(data, textStatus, XMLHttpRequest)], [dataType])
And the check looks like:
if ( jQuery.isFunction( data ) ) {
type = type || callback;
callback = data;
data = null;
}
jQuery.isFuncton() is just a repeatedly used shortcut for jQuery.type(obj) === "function" which is really using Object.toString(), but this used to be done with typeof() directly.
You can see the full source from jQuery 1.4.4 here.

How about just opening the file containing the culprit code?
My guess is that it's either doing type-checking on the variables (using typeof or such), or it's using arguments.length to determine how many arguments were passed, and using that to choose from a list of predefined allowed parameters.

JavaScript does not support method overloading through the use of different method signatures.
You can, in fact, pass 0 arguments to methods that list one or more, or pass more arguments than are listed.
All arguments passed to a method can be accessible through the keyword 'arguments' which behaves like an array.
That being said, you can then check for the presence of arguments directly or via the 'arguments' array.
function ABC(arg1, arg2, arg3){
if(typeof(arg2) === 'undefined') { }
if(typeof(arg3) === 'function'){ }
// arguments[0] == arg1, etc
}
ABC(1) ; // arg2 and arg3 are undefined
ABC(1,2,3,4); // arg1, arg2, and arg3 are defined, plus arguments[3] === 4
Knowing the above, you can therefore figure out how many arguments were provided, and use typeof() calls to determine what type.

In JavaScript, the argument list in a function definition doesn't force you to call the function with exactly those arguments:
// Given this definition....
function foo(one, two){
}
// ... all these calls are valid:
foo();
foo(1);
foo(1, 2);
foo(1, 2, 3);
And, of course, JavaScript is loosely typed as well:
foo(1, 2);
foo("Hello", "World");
foo(new Date, {foo: "bar"});
Using these two concepts, the language allows you to overload methods to your will:
function foo(){
var info = "Argument list:\n";
for(var i=0, len=arguments.length; i<len; i++){
info += "- Argument #" + (i+1) + " is a " + typeof(arguments[i]) + "\n";
}
alert(info);
}
foo(1, "1", {}, [], function(){});
Gives:
Argument list:
- Argument #1 is a number
- Argument #2 is a string
- Argument #3 is a object
- Argument #4 is a object
- Argument #5 is a function

+1 for Jani's answer. Just check the type of each parameter, and adjust your logic accordingly.

Related

what is this function for? [duplicate]

This snippet is cut from Secrets of the JavaScript Ninja.
function log() {
try {
console.log.apply( console, arguments );
} catch(e) {
try {
opera.postError.apply( opera, arguments );
} catch(e){
alert( Array.prototype.join.call( arguments, " " ) );
}
}
}
Why should I use apply and what's the difference between console.log.apply(console, arguments) and console.log(arguments)?
In this case, the log function may accept any number of arguments.
Using .apply(), it doesn't matter how many arguments are passed. You can give the set to console.log(), and they will arrive as individual arguments.
So if you do:
console.log(arguments)
...you're actually giving console.log a single Arguments object.
But when you do:
console.log.apply( console, arguments );
...it's as though you passed them separately.
Other useful examples of using .apply() like this can be demonstrated in other methods that can accept a variable number of arguments. One such example is Math.max().
A typical call goes like this:
var max = Math.max( 12,45,78 ); // returns 78
...where it returns the largest number.
What if you actually have an Array of values from which you need the largest? You can use .apply() to pass the collection. Math.max will think they were sent as separate arguments instead of an Array.
var max = Math.max.apply( null, [12,45,92,78,4] ); // returns 92
As you can see, we don't need to know in advance how many arguments will be passed. The Array could have 5 or 50 items. It'll work either way.
If you have
function log() {
console.log.apply(console, arguments);
}
and call it like log('foo'); then that translates to console.log.apply(console, ['foo']); which is equivalent to console.log('foo'); which is what you want.
If you defined it like
function log() {
console.log(arguments);
}
instead then log('foo'); would be equivalent to log(['foo']); which is not what you want.
The apply function changes the value of this in the callee as well as letting you pass an array for the arguments.
For example, if you want to pass an array as arguments to a function:
function foo(value1, value2, value3) {
alert("Value 1 is "+value1+".");
alert("Value 2 is "+value2+".");
alert("Value 3 is "+value3+".");
}
var anArray=[1, 2, 3];
foo(anArray); // This will not work. value1 will be anArray, and value 2 and 3 will be undefined.
foo.apply(this, anArray); // This works, as anArray will be the arguments to foo.
Or, another use: changing this:
function Foo() {
this.name="world";
this.sayHello=function() {
alert("Hello, "+this.name);
};
}
var foo=new Foo();
foo.sayHello(); // This works, as this will be foo in foo's sayHello.
var sayHello=foo.sayHello;
sayHello(); // This does not work, as this will not be foo.
sayHello.apply(foo, []); // This will work, as this will be foo.
Let's discuss some background, why apply exist specially when we have call method with similar syntax.
First we need to understand some topics:
Variadic function:
In computer programming it is a function which can accept any number
of arguments.
Data structure is JavaScript:
In javascript if we are dealing with data than most commonly used data structures is array, and in most cases we get data in the form of array.
Now if we are executing any variadic function in javascript than our call will look like this -
average is a variadic function, and its call will look like,
average(1,2,3);
average(1);
average(3,5,6,7,8);
and if are getting data in array format(in most of the cases we will get data in array format for Variadic functions), than we need to call our function like -
average(array[0],array[1],array[2],array[3],.... and so on)
What if we got an array with length 100 items, do we write it like this ?
No, We have apply method, which was designed just for this purpose.
average.apply(null,array);
console.log(arguments) would send a single argument to console.log, the array of arguments passed to your log method. console.log.apply(console, arguments) sends the possibly multiple arguments as multiple arguments instead of a single array.

Why should use "apply"?

This snippet is cut from Secrets of the JavaScript Ninja.
function log() {
try {
console.log.apply( console, arguments );
} catch(e) {
try {
opera.postError.apply( opera, arguments );
} catch(e){
alert( Array.prototype.join.call( arguments, " " ) );
}
}
}
Why should I use apply and what's the difference between console.log.apply(console, arguments) and console.log(arguments)?
In this case, the log function may accept any number of arguments.
Using .apply(), it doesn't matter how many arguments are passed. You can give the set to console.log(), and they will arrive as individual arguments.
So if you do:
console.log(arguments)
...you're actually giving console.log a single Arguments object.
But when you do:
console.log.apply( console, arguments );
...it's as though you passed them separately.
Other useful examples of using .apply() like this can be demonstrated in other methods that can accept a variable number of arguments. One such example is Math.max().
A typical call goes like this:
var max = Math.max( 12,45,78 ); // returns 78
...where it returns the largest number.
What if you actually have an Array of values from which you need the largest? You can use .apply() to pass the collection. Math.max will think they were sent as separate arguments instead of an Array.
var max = Math.max.apply( null, [12,45,92,78,4] ); // returns 92
As you can see, we don't need to know in advance how many arguments will be passed. The Array could have 5 or 50 items. It'll work either way.
If you have
function log() {
console.log.apply(console, arguments);
}
and call it like log('foo'); then that translates to console.log.apply(console, ['foo']); which is equivalent to console.log('foo'); which is what you want.
If you defined it like
function log() {
console.log(arguments);
}
instead then log('foo'); would be equivalent to log(['foo']); which is not what you want.
The apply function changes the value of this in the callee as well as letting you pass an array for the arguments.
For example, if you want to pass an array as arguments to a function:
function foo(value1, value2, value3) {
alert("Value 1 is "+value1+".");
alert("Value 2 is "+value2+".");
alert("Value 3 is "+value3+".");
}
var anArray=[1, 2, 3];
foo(anArray); // This will not work. value1 will be anArray, and value 2 and 3 will be undefined.
foo.apply(this, anArray); // This works, as anArray will be the arguments to foo.
Or, another use: changing this:
function Foo() {
this.name="world";
this.sayHello=function() {
alert("Hello, "+this.name);
};
}
var foo=new Foo();
foo.sayHello(); // This works, as this will be foo in foo's sayHello.
var sayHello=foo.sayHello;
sayHello(); // This does not work, as this will not be foo.
sayHello.apply(foo, []); // This will work, as this will be foo.
Let's discuss some background, why apply exist specially when we have call method with similar syntax.
First we need to understand some topics:
Variadic function:
In computer programming it is a function which can accept any number
of arguments.
Data structure is JavaScript:
In javascript if we are dealing with data than most commonly used data structures is array, and in most cases we get data in the form of array.
Now if we are executing any variadic function in javascript than our call will look like this -
average is a variadic function, and its call will look like,
average(1,2,3);
average(1);
average(3,5,6,7,8);
and if are getting data in array format(in most of the cases we will get data in array format for Variadic functions), than we need to call our function like -
average(array[0],array[1],array[2],array[3],.... and so on)
What if we got an array with length 100 items, do we write it like this ?
No, We have apply method, which was designed just for this purpose.
average.apply(null,array);
console.log(arguments) would send a single argument to console.log, the array of arguments passed to your log method. console.log.apply(console, arguments) sends the possibly multiple arguments as multiple arguments instead of a single array.

How to detect a JavaScript function with a certain signature has been registered?

Say you have two functions with the following signatures:
addClass( class )
addClass( class, duration )
These live in a third party library (no prizes for guessing which!!).
If you call addClass() with two parameters but only have signature 1 registered, there will be no error and signature 1 will be called. The second parameter is ignored.
So is there a way to detect that signature 2 with two parameters has been registered?
You can use the length property of the function object to check the signature. Example:
function x(a) {}
function y(a,b) {}
alert(x.length); // shows "1"
alert(y.length); // shows "2"
There is no native method overloading in JavaScript. You can create your own, though: http://ejohn.org/blog/javascript-method-overloading/
(Update 11/5/15: The link seems to be dead, here's the Google Cache version)
So if you do
function addClass( class ) { console.log('1 arg'); };
function addClass( class, duration ) { console.log('2 args'); };
the second one overwrites the first one. So even if you call "addClass(1)", the output will still be "2 args". Same as doing
someObject.addClass = function(a) {...}
someObject.addClass = function(a, b) {...}
The first "version" will be lost.
It isn't very difficult to achieve, but Resig's method isn't up to much, as it only checks for arguments length, which is only tangentially related to real call signatures.
Johannes method is actually method overriding not multiple call signatures, and anyway will fail in JS Strict mode where multiple function declaration of the same name are forbidden.
To implement properly you'll need to implement it via call forwarding, wrapping each call signature in a type/existence check:
function myFunction(arg1, arg2, arg3){
switch(true){
case arg1 instanceof someObject:
myFunction_signature1.apply(this, arguments);
break;
case typeof arg1 === "string":
myFunction_signature2.apply(this, arguments);
break;
}
}
It gets a little more complex when some params are optional as well.
If you are using node you can use func.toString() and parse the result to get the signature that was being used.

What does this Code do?

i'm reading through jQuery's "Plugins/Authoring" though i already wrote a few jQuery-Plugins. Now I see that jQuery has a special way of scoping the methods and calling:
(function( $ ){
var methods = {
init : function( options ) { // THIS },
show : function( ) { // IS },
hide : function( ) { // GOOD },
update : function( content ) { // !!! }
};
$.fn.tooltip = function( method ) {
// Method calling logic
if ( methods[method] ) {
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.tooltip' );
}
};
})( jQuery );
I understand the concept of what will happen in the end… but how exactly? This part is what confuses me:
// Method calling logic
if ( methods[method] ) {
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
}
Why Array.prototype.slide.call(argumetns, 1)? And where does the variable "arguments" come from all of the sudden? Any brief or deeper explanation is much appreciated. It is said, that this is how plugins should be written… so i'd like to know why.
Thanks!
arguments
arguments is a part of the JavaScript language. I was confused in exactly the way you were when I first ran into it; it's not just you. :-) It's an automatic local variable in every function, and is an array-like structure giving you all of the arguments (see Section 10.6 of the spec), e.g.:
function foo() {
var index;
for (index = 0; index < arguments.length; ++index) {
alert(arguments[index]);
}
}
foo("one", "two"); // alerts "one", then alerts "two"
When I say arguments is array-like, I mean it — it's not an Array. Its references to the arguments are live (and bidirectional). For instance:
function foo(namedArg, anotherNamedArg) {
alert(namedArg === arguments[0]); // alerts true, of course
alert(anotherNamedArg === arguments[1]); // also alerts true
namedArg = "foo";
alert(arguments[0]); // alerts "foo"
arguments[0] = "bar";
alert(namedArg); // alerts "bar"
}
Note that when assigning a value to namedArg, the result is reflected in arguments[0], and vice-versa.
arguments is really cool, but only use it if you need to — some implementations speed up calling functions by not hooking it up until/unless the function actually first tries to access it, which can slow the function down (very slightly).
arguments also has property on it called callee, which is a reference to the function itself:
function foo() {
alert(foo === arguments.callee); // alerts true
}
However, it's best to avoid using arguments.callee for several reasons. One reason is that in many implementations, it's really slow (I don't know why, but to give you an idea, the function call overhead can increase by an order of magnitude if you use arguments.callee). Another reason is that you can't use it in the new "strict" mode of ECMAScript5.
(Some implementations also had arguments.caller — shudder — but fortunately it was never widespread and is not standardized anywhere [nor likely to be].)
The slice call and apply
Regarding
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
What that's doing is using the Array#slice method to copy the arguments into an array (minus the first argument, which was the method to call), and then passing the resulting array into the Function#apply function on the function instance it's calling. Function#apply calls the function instance with the given this object and the arguments supplied as an array. The code's not just using arguments.slice because (again) arguments isn't really an Array and so you can't rely on it having all of the Array functions, but the specification specifically says (in Section 15.4.4.10) that you can apply the Array.prototype.slice function to anything that's array-like, and so that's what they're doing.
Function#apply and Function#call are also built-in parts of JavaScript (see Sections 15.3.4.3 and 15.3.4.4). Here are simpler examples of each:
// A function to test with
function foo(msg, suffix) {
alert(this.prefix + ": " + msg + suffix);
}
// Calling the function without any `this` value will default `this`
// to the global object (`window` on web browsers)
foo("Hi there", "!"); // Probably alerts "undefined: Hi there!" because the
// global object probably doesn't have a `prefix` property
// An object to use as `this`
var obj = {
prefix: "Test"
};
// Calling `foo` with `this` = `obj`, using `call` which accepts the arguments
// to give `foo` as discrete arguments to `call`
foo.call(obj, "Hi there", "!"); // alerts "Test: Hi there!"
// ^----^-----------^---- Three discrete args, the first is for `this`,
// the rest are the args to give `foo`
// Calling `foo` with `this` = `obj`, using `apply` which accepts the arguments
// to give `foo` as an array
foo.apply(obj, ["Hi there", "!"]); // alerts "Test: Hi there!"
// ^---------------^---- Note that these are in an array, `apply`
// takes exactly two args (`this` and the
// args array to use)
arguments is a keyword, the arguments passed to the function. But you don't want all of them, since the first you know, it's method, so this is taking every argument past the first to use in .apply()...passing those arguments into whichever method was specified in the first method argument.
If it can't find method (meaning the first argument wasn't 'init', 'show', 'hide', or 'update', then it goes to the else portion, and passes all the arguments to the init method (the default, if you will).
For example:
.tooltip({ thing: value }) would call init({ thing: value }) since that's the default
.tooltip('show', var1, var2) would call show(var1, var2)
Variable arguments is defined javascript variable. It stores all arguments called in function in array. For example :
function variablesCount() {
alert(arguments.length);
}
variablesCount(var1, var2); // alerts 2
Array.prototype.slide.call(argumetns, 1) converts the arguments pseudo-array into a real array (skipping the first element).
arguments contains all arguments the function you are currently in has been called with. It is automatically provided by JavaScript. Since we need a real array (arguments cannot be manipulated, etc.), we convert it to one using this statement.
arguments is a special JavaScript variable which means "all the arguments given to this function". It isn't quite an array, but behaves almost like one, so instead of saying arguments.slice(1) it calls Array.prototype.slice.call(arguments, 1); what it's doing it taking all except the first value in the list - for func(1, 2, 3, 4) it will be [2, 3, 4].
The end result of this is that when you call $.fn.tooltip('foo', 'bar', 'baz') it will try to call methods['foo']('bar', 'baz').

Javascript not passing all the parameters

is that possible to call Javascript function without supply all the parameters?
I come across a line of code doesn't make much sense unless I assume that in Javascript supply all the parameters are not required?
The parameter been missed is a boolean value, so could I further assume that undefined boolean value in Javascript equal to 'false'?
Yes, the other parameters will just be undefined if they're not passed in :)
For example:
function myFunc(param1, param2) {
alert(param1);
alert(param2);
}
This is a valid call:
myFunc("string"); //alerts "string" then undefined
Give it a try here. If the check in your question is something like if(!param2), it'll evaluate to true, since undefined ~= false for most purposes. It's worth noting this is not only acceptable, it's very common, almost every library or framework expects only some of the parameters to be passed into most of their functions.
Adding to Nick's response, you could have:
// set the value to false if not passed
if (typeof(param2) === "undefined") param2 = false;
You may also use Variadic Functions in javascript. You can actually pass any type/number of parameters to any javascript function and use arguments to retrieve those parameters.
function PrintList()
{
for (var i = 0; i < arguments.length; i++)
{
document.write(arguments[i] + "<br />");
}
}
// Calls to Function
PrintList('Google');
PrintList('Google', 'Microsoft', 'Yahoo');
PrintList('Google', 'Microsoft', 'Yahoo', 'Adobe');

Categories

Resources