A function using parameters but not included in declaration? [duplicate] - javascript

Is there a way to allow "unlimited" vars for a function in JavaScript?
Example:
load(var1, var2, var3, var4, var5, etc...)
load(var1)

Sure, just use the arguments object.
function foo() {
for (var i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
}

In (most) recent browsers, you can accept variable number of arguments with this syntax:
function my_log(...args) {
// args is an Array
console.log(args);
// You can pass this array as parameters to another function
console.log(...args);
}
Here's a small example:
function foo(x, ...args) {
console.log(x, args, ...args, arguments);
}
foo('a', 'b', 'c', z='d')
=>
a
Array(3) [ "b", "c", "d" ]
b c d
Arguments
​ 0: "a"
​1: "b"
​2: "c"
​3: "d"
​length: 4
Documentation and more examples here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters

Another option is to pass in your arguments in a context object.
function load(context)
{
// do whatever with context.name, context.address, etc
}
and use it like this
load({name:'Ken',address:'secret',unused:true})
This has the advantage that you can add as many named arguments as you want, and the function can use them (or not) as it sees fit.

I agree with Ken's answer as being the most dynamic and I like to take it a step further. If it's a function that you call multiple times with different arguments - I use Ken's design but then add default values:
function load(context) {
var defaults = {
parameter1: defaultValue1,
parameter2: defaultValue2,
...
};
var context = extend(defaults, context);
// do stuff
}
This way, if you have many parameters but don't necessarily need to set them with each call to the function, you can simply specify the non-defaults. For the extend method, you can use jQuery's extend method ($.extend()), craft your own or use the following:
function extend() {
for (var i = 1; i < arguments.length; i++)
for (var key in arguments[i])
if (arguments[i].hasOwnProperty(key))
arguments[0][key] = arguments[i][key];
return arguments[0];
}
This will merge the context object with the defaults and fill in any undefined values in your object with the defaults.

It is preferable to use rest parameter syntax as Ramast pointed out.
function (a, b, ...args) {}
I just want to add some nice property of the ...args argument
It is an array, and not an object like arguments. This allows you to apply functions like map or sort directly.
It does not include all parameters but only the one passed from it on. E.g. function (a, b, ...args) in this case args contains
argument 3 to arguments.length

Yes, just like this :
function load()
{
var var0 = arguments[0];
var var1 = arguments[1];
}
load(1,2);

As mentioned already, you can use the arguments object to retrieve a variable number of function parameters.
If you want to call another function with the same arguments, use apply. You can even add or remove arguments by converting arguments to an array. For example, this function inserts some text before logging to console:
log() {
let args = Array.prototype.slice.call(arguments);
args = ['MyObjectName', this.id_].concat(args);
console.log.apply(console, args);
}

Although I generally agree that the named arguments approach is useful and flexible (unless you care about the order, in which case arguments is easiest), I do have concerns about the cost of the mbeasley approach (using defaults and extends). This is an extreme amount of cost to take for pulling default values. First, the defaults are defined inside the function, so they are repopulated on every call. Second, you can easily read out the named values and set the defaults at the same time using ||. There is no need to create and merge yet another new object to get this information.
function load(context) {
var parameter1 = context.parameter1 || defaultValue1,
parameter2 = context.parameter2 || defaultValue2;
// do stuff
}
This is roughly the same amount of code (maybe slightly more), but should be a fraction of the runtime cost.

While #roufamatic did show use of the arguments keyword and #Ken showed a great example of an object for usage I feel neither truly addressed what is going on in this instance and may confuse future readers or instill a bad practice as not explicitly stating a function/method is intended to take a variable amount of arguments/parameters.
function varyArg () {
return arguments[0] + arguments[1];
}
When another developer is looking through your code is it very easy to assume this function does not take parameters. Especially if that developer is not privy to the arguments keyword. Because of this it is a good idea to follow a style guideline and be consistent. I will be using Google's for all examples.
Let's explicitly state the same function has variable parameters:
function varyArg (var_args) {
return arguments[0] + arguments[1];
}
Object parameter VS var_args
There may be times when an object is needed as it is the only approved and considered best practice method of an data map. Associative arrays are frowned upon and discouraged.
SIDENOTE: The arguments keyword actually returns back an object using numbers as the key. The prototypal inheritance is also the object family. See end of answer for proper array usage in JS
In this case we can explicitly state this also. Note: this naming convention is not provided by Google but is an example of explicit declaration of a param's type. This is important if you are looking to create a more strict typed pattern in your code.
function varyArg (args_obj) {
return args_obj.name+" "+args_obj.weight;
}
varyArg({name: "Brian", weight: 150});
Which one to choose?
This depends on your function's and program's needs. If for instance you are simply looking to return a value base on an iterative process across all arguments passed then most certainly stick with the arguments keyword. If you need definition to your arguments and mapping of the data then the object method is the way to go. Let's look at two examples and then we're done!
Arguments usage
function sumOfAll (var_args) {
return arguments.reduce(function(a, b) {
return a + b;
}, 0);
}
sumOfAll(1,2,3); // returns 6
Object usage
function myObjArgs(args_obj) {
// MAKE SURE ARGUMENT IS AN OBJECT OR ELSE RETURN
if (typeof args_obj !== "object") {
return "Arguments passed must be in object form!";
}
return "Hello "+args_obj.name+" I see you're "+args_obj.age+" years old.";
}
myObjArgs({name: "Brian", age: 31}); // returns 'Hello Brian I see you're 31 years old
Accessing an array instead of an object ("...args" The rest parameter)
As mentioned up top of the answer the arguments keyword actually returns an object. Because of this any method you want to use for an array will have to be called. An example of this:
Array.prototype.map.call(arguments, function (val, idx, arr) {});
To avoid this use the rest parameter:
function varyArgArr (...var_args) {
return var_args.sort();
}
varyArgArr(5,1,3); // returns 1, 3, 5

Use the arguments object when inside the function to have access to all arguments passed in.

Be aware that passing an Object with named properties as Ken suggested adds the cost of allocating and releasing the temporary object to every call. Passing normal arguments by value or reference will generally be the most efficient. For many applications though the performance is not critical but for some it can be.

Use array and then you can use how many parameters you need. For example, calculate the average of the number elements of an array:
function fncAverage(sample) {
var lenghtSample = sample.length;
var elementsSum = 0;
for (var i = 0; i < lenghtSample; i++) {
elementsSum = Number(elementsSum) + Number(sample[i]);
}
average = elementsSum / lenghtSample
return (average);
}
console.log(fncAverage([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])); // results 5.5
let mySample = [10, 20, 30, 40];
console.log(fncAverage(mySample)); // results 25
//try your own arrays of numbers

Related

pass a parameter to a function without an order

I have a function like this:
let showNotification = function(a,b,c,d,e,f){
console.log(a,b,c,d,e,f);
}
While calling this function, I know I need to follow order, something like this (if I dont want to pass param for c,d,e):
showNotification(1,2,,,,6);
But this forces me to make sure I pass all parameter and it should be in order..
I am looking for a better way to pass param in this case.
I know that I can create an object of param instead where instead of passing the values. something like this:
let showNotification = function(objectParam){
console.log(objectParam.a, objectParam.b, objectParam.c, objectParam.d, objectParam.e, objectParam.f)
}
then call it:
showNotification({a: 1, b: 2, e:6});
I can pass whole object but this is not what I am looking. I do not want to create object every time for this.
Thinking loud, if there is a way to pass the string values, without taking care of the order.
I see some dead SO posts regarding this, but no one has solution.
#T,J. Crowder answer said better than this. In other way, you can do this by using spread Operators. As you said the thing is here all are optional, except the first param. So you should pass first parameter value. then you don't need to pass other value unless you want.But you should mention the position of parameter value as empty with , in an array. In my case you should create an object or array.
For example . If you have a dynamic array with values(whatever but position should be mandatory )and position [1,,,3]. then spread operator helps to merge that dynamic array with your function parameters.
let showNotification = function(a,b,c,d,e,f){
console.log(a,b,c,d,e,f);
};
let parts = [2,3,,,6];//Think it is a dynamic array on run time or compile time
showNotification(1,...parts);
And here the #Felix Kling answers could help by using Named Parameters
var parameterfy = (function() {
var pattern = /function[^(]*\(([^)]*)\)/;
return function(func) {
// fails horribly for parameterless functions ;)
var args = func.toString().match(pattern)[1].split(/,\s*/);
return function() {
var named_params = arguments[arguments.length - 1];
if (typeof named_params === 'object') {
var params = [].slice.call(arguments, 0, -1);
if (params.length < args.length) {
for (var i = params.length, l = args.length; i < l; i++) {
params.push(named_params[args[i]]);
}
return func.apply(this, params);
}
}
return func.apply(null, arguments);
};
};
}());
var foo = parameterfy(function(a, b, c) {
console.log('a is ' + a, ' | b is ' + b, ' | c is ' + c);
});
foo(1, 2, 3); // a is 1 | b is 2 | c is 3
foo(1, {b:2, c:3}); // a is 1 | b is 2 | c is 3
foo(1, {c:3}); // a is 1 | b is undefined | c is 3
foo({a: 1, c:3}); // a is 1 | b is undefined | c is 3
More examples:
Pass a value to a specific parameter without caring about the position of the parameter
Passing the argument name while calling function in javascript
JavaScript: Get Argument Value and NAME of Passed Variable
Bhushan Babar hasn't posted his suggestion as a an answer, so I'll post it as a community wiki:
You can prepend or append(concat) tokens for respective parameter into your strings , e.g if you want to send parameter which represents parameter d , and string you want to pass is "myString", then you can decide format of your token like $&d&$ , so your parameter will look like "myString$&d&$"
TL;DR I know you didn't want to use an object, but modern JavaScript engines are really fast at creating and disposing of objects, and with parameter defaults and parameter destructuring, I think an options object is your best option. See "Notes on passing in an object (defaults, destructuring)" below for details.
You've covered two of the main options (I'm going to make some notes on the passing-in-an-object version below). A third option is to adopt the a variant of builder pattern, but it involves using an object too. You've said you don't want to do that, but haven't said why not. Note that modern JavaScript engines create and dispose of objects really, really fast if that's your concern.
Marker strings
A fourth option occurred to me when reading Bhushan Babar's append/prepend idea: Instead of doing that, you could use marker strings in the arguments list to indicate what the next argument is, e.g.:
showNotification("the required parameter", "a", value_for_a, "c", value_for_c);
On first glance, that doesn't involve creating an object, but on modern JavaScript engines, processing it will create an object that isn't otherwise created: arguments, the pseudo-array of passed-in arguments. Because that (or a rest parameter, which also creates an object) is the only reasonable way you could consume such a thing.
Variant on Builder
In this approach, the main function returns a builder object with setters for the various options, and then a final "Yep, we're ready" call that starts the process (where in the builder pattern that final call usually builds the final object). Using it would look something like this:
showNotification("the required param")
.withA(value_for_a)
.withC(value_for_c)
.go();
Implementing this is complicated relative to other approaches, but not difficult.
Notes on passing in an object (defaults, destructuring)
If you do use an object (despite not wanting to), you can use default parameters and destructuring to make the object more convenient to work with:
let showNotification = function({a = 1, b = 2, c = 3, d = 4, e = 5, f = 6} = {/*default if no object at all*/a: "foo"}){
console.log(a,b,c,d,e,f);
};
showNotification();
showNotification({});
showNotification({a:42});
In a comment you've said:
the thing is here all are optional, except the first param
That sounds like you probably want the first param and then an options object:
let showNotification = function(firstThing, {a = "default_a", b = "default_b"/*etc.*/} = {}) {
console.log(firstThing, a, b);
};

Trying to understand (function(func) { func(); } in JavaScript

Coming from C, every function needs a name that is used to call that specific function. but in the JavaScript code below. There is no name at all. So how does function(func) { func(); } calls the something.push(function() ?
Not to mention that something.push(function() doesn't even have an argument, which from C perspective, this is wrong
var something = [],
object = {
a: true,
b: true,
c: true
}
for (let key in object){
something.push(function() {
console.log(key);
});
}
something.forEach(function(func) { func(); }):
Coming from C, every function needs a name that is used to call that specific function. but in the JavaScript code below, there is no name at all.
Well in JavaScript, that's just not the case – functions can indeed be anonymous (nameless) functions. Sometimes you'll hear them called "lambdas" or redundantly called "lambda functions".
But more importantly, functions are first class data members in JavaScript, meaning:
They can be assigned to variables
let y = x => x + 5
y(3)
// 8
They can be passed as arguments to other functions
let xs = [1,2,3]
let y = x => 2 * x
xs.map(y)
// [2,4,6]
They can be returned from a function
let add = x => {
return y => x + y
}
let f = add(1)
f(2)
// 3
They can be included in data structures
let pikachu = {
number: 25,
shock: enemy => enemy.damage(30),
growl: enemy => enemy.sadden(40)
}
pikachu.growl(meowth)
// "It's very effective!"
So what's the take away here? Well, in JavaScript, you need to think of functions as being no different than any other value (eg) 1, "foo", or [{user: 'bob'}, {user: 'alice'}] – conceptually, they are just data and they're all first class
How to understand your code
Here's your code affectionally reformatted by me. Since you originally asked about ES6, I'm going to substitute your lambdas with ES6 arrow functions
let something = []
let object = { a: true, b: true, c: true }
for (let key in object) {
something.push(() => console.log(key))
}
something.forEach(func => func())
// a
// b
// c
Using high level descriptors, the nature of this code is
iterate thru each key of object, add some value to something
iterate thru each value of something, do something with that value
In your case, some value is a function, but I want to show you what your code would look like if we used another kind of value
let something = []
let object = { a: true, b: true, c: true }
for (let key in object) {
// this time push a string, instead of an anonymous function
something.push("the key is: " + key)
}
something.forEach(str => console.log(str))
// the key is: a
// the key is: b
// the key is: c
So now we can see how the code works when a different value type (String) is used. Aside from using a string, the only other thing we did was change
// from
func => func()
// to
str => console.log(str)
The reasoning here is
in your original code, something is an array of functions, so for each of those functions, func, we call func()
in the modified code, something is an array of strings, so for each of those strings, str, we call console.log(str)
Dissection of Array.prototype.forEach
The last remaining bit to understand is the super-powered forEach function. Remember point number 2 in the first class capabilities: first class data members can be passed as arguments to other functions.
forEach expects its argument to be a function. So forEach is a function that accepts another function. This kind of function is called a higher-order function.
Don't let these fancy names distract you from their underlying simplicity. Making our own higher-order functions like forEach is actually really easy. Always just remember that functions are like any other value – below, we'll write our own version of forEach so you can see how it works
let something = []
let object = { a: true, b: true, c: true }
// forEach accepts an array and some function, f
let forEach = (arr, f) => {
// iterate thru each value of arr
for (let value of arr) {
// call f on each value
f(value)
}
}
for (let key in object) {
something.push("the key is: " + key)
}
forEach(something, str => console.log(str))
// the key is: a
// the key is: b
// the key is: c
So now we see the string array working with our own forEach function. Let's make sure it works with your original lambda array, too.
let something = []
let object = { a: true, b: true, c: true }
let forEach = (arr, f) => {
for (let value of arr) {
f(value)
}
}
for (let key in object) {
something.push(() => console.log(key))
}
forEach(something, func => { func() })
// a
// b
// c
Remarks
And that's it! Functions aren't special snowflakes in JavaScript. They're just like all the datums.
Coming from a C perspective, it might take awhile to think about functions in this way. But really, you should leave everything you understand about any language at the door when you're walking into the home of any new programming language. Each language has its own way of expressing itself yielding a unique set of strengths and weaknesses. By bringing in a "strength" of some language A to another language B, you might be substituting one of B's own strengths for one of it's weaknesses.
JavaScript is a multi-paradigm language making it rather suitable for imperative, object-oriented, and functional programming styles. Because of its jack-of-all-trades nature, it's probably not the best oop or fp language, but it's still rather impressive that it can express a particular solution to a problem in a wide variety of ways.
The something array is storing references to functions, but not invoking them. In the forEach loop, each function within the array is being invoked.
To be fair, the naming is confusing. You could just as easily write:
something.forEach(function(storedFunction) { storedFunction(); }):
since it's a temporary variable
In JavaScript, functions are treated as objects. And in C you must be aware that not every object (value) needs to have a name, or doesn't need to be stored in a variable. Just like you can have​ sum (4, 5);. 4 and 5 are values not stored in variables.
To answer your question about how they will be called if they have no name, the function is passed as an argument to the push function. There the formal parameter must have some name. So it can be called by that name. In this case push doesn't call it but stores it in an array to be called later. It can be called by indexing into the array.
JavaScript is more abstract language than C. In JavaScript interpretter internally saves types and pointers of every variable. When you declare a function, it is internally saved somewhere in memory and pointer of that function is passed to whatever you passed it (here to something.forEach).
But, this is not pointer like in C, you cannot access its value (function address) nor you can change it. In JavaScript these pseudo-pointers are called "references". Simply, when you try to call something, interpretter engine will check its type. If its type type is a function, thread will do some preparations and then execute jump to memory location which function pointer is pointing to.
So, what's happening here is in the loop, we are pushing a function to something on each iteration. Assuming something is an array in this case.
Then, we call the collection's foreach() function. The first argument in this function is always the current item over which we are iterating. In this case, we're iterating over the list of functions we built in the loop. Since these are functions, we can call them, which is what's happening for each one.
Here's an example to help illustrate:
// declare some functions
function a(){console.log('hi');}
function b(){console.log('there');}
function c(){console.log('friend');}
const ar = [];
// push the functions to the array
ar.push(a);
ar.push(b);
ar.push(c);
ar.push(a);
ar.forEach(function(func) {func();});
// console output:
// hi
// there
// friend
// hi
EDIT: I highly recommend you read Mozilla's documentation of the function type. If you go there and thoroughly read through it I think you'll have a very solid grasp of this concept.
What you see is the usage of an anonymous function and a closure. It's pretty Javascript in a nutshell.
There I describe the code block per block :
We create an array (like in C) :
var something = [],
We create an object (which is a collection of key=value)
object = {
a: true,
b: true,
c: true
}
We iterate through the elements of the object object
for (let key in object) {
There the show is on :
something.push(function() {
Each object has a push function that allow to put elements dynamically inside of it.
What you see here is a anonymous function being used as an argument; it means that for each executions of this push function, a function that do :
console.log(key);
will be put inside the something array;
There after, the only thing that is done is a callback with func() :
something.forEach(function(func) { func(); }):
This means forEach elements of something, you will treat it as a function resulting in calling the precedent function stored with push()

Javascript: confuse about usage of function call method

I achieve a forEach function:
function forEach(arr, fn) {
for (var i = 0; i < arr.length; i++) {
fn.call({}, arr[i], i);
}
}
what I confused is about fn.call({}, arr[i], i);
the first parameter is pass empty just like above {} is better
or pass this in: fn.call(this, arr[i], i); is better?
Or it doesn't matter
It matters quite a bit. The first parameter to .call() is the value to be used for this inside the called function. Thus, it doesn't make sense to talk about what value is "better"; the right value to pass is the one you need in order for the called function to operate properly.
For example, if you want to call a function on the Array prototype, then the value of this inside that function has to be something that "feels like" an array (a "length" property and numerically-indexed properties). Thus:
var sneaky = {
"0": "hello",
"1": "world",
"length": 2
};
alert( Array.prototype.join.call(sneaky, " - ") ); // "hello - world"
That works because that function expects this to refer to the array to be joined.
There are as many other examples as there are functions that have expectations about this. In your sample code, passing {} gives the called function a this reference to that newly-created empty object. Will that work? I don't know, because that function could expect anything. There's no way to find out, either, except by looking at the code (or trusting documentation). If all you know is that it's some random arbitrary function, then {} is a reasonable guess, though undefined might be better, to force early failure.
Personally I would go with passing this. By passing {} you are limiting the flexibility of your function. You will never be able to bind another object to this function the way it is currently written. This won't work:
forEach.call(newContext, array, fn)
Neither will this:
forEach(array, fn.bind(newContext));
By binding {} inside your forEach you are adding unexpected behavior.

Javascript Callbacks with custom aguments

I am trying to work out how to implement an asynchronous callback in node given that I don't know what the arguments could be.
I'll state the sync version of what I'm trying to do so as to make this clearer.
function isAuthorized(userID){
// check for user Permissions here
return isAuthorized;
}
Using this any function can call it and, from the value it returns, determine if the user is authorized. However, the function that I use to get the userID (and check for permissions) are both asyncronous functions and require callbacks. However I have no idea how to get this implemented when I don't know the arguments needed in the callback.
The function that calls this could want to send an number of arguments to the callback (normally it'd just wait for a return in sync) and that's what's confusing me.
function isAuthorized(socket, callback, args){
socket.get('userID', function (err, userID) {
//check userID for permission
callback(args);
});
}
I realize I could convert the arguments into an array and send that array, but I wanted to know if there is a more general way to do this that doesn't force me to make all my callbacks convert from an array of arguments.
Hope you can help,
Pluckerpluck
You can always create a function to pull those arguments into an array for you, and then reuse it in each of your async functions:
function asyncWithCallbackArgs(obj, callback) {
var args = Array.prototype.slice.call(arguments, 2);
callback.apply(obj, args);
}
This will then enable you to do things like this:
function callback (c, d, e, f) {
console.log([c, d, e, f]);
}
asyncWithCallbackArgs({}, callback,'c','d','e','f');​
There's a fiddle here to play with.
I'm not sure, if I understand you the right way, but it seems you're looking for the arguments object in a function (MDN link).
Basically that way you don't have to specify any parameters in advance, but have the function look for what parameters are present. So you can do something like the following:
function myFunc() {
for (var i=0; i<arguments.length; ++i ) {
console.log( arguments[i] );
}
}
Which results in outputs like
myFunc( 1 );
> 1
myFunc( 's', 3, 'S' );
> "s"
> 3
> "S"
So when you build an API, you specify what order the arguments are passed into the callback. So, you will want to comment your code well so that anyone who uses your API can understand what they will be getting back from your method.
It almost sounds like you are perhaps wanting to pass back different arguments, depending on what the passed in callback function looks like. That isn't traditionally how an API works. It would take a considerable amount of time to do that effectively.
I would recommend passing an object to the callback. The Object can have multiple arguments that people can pull out of it. This would ease future enhancement, as you could very simply add an additional property to that object without messing up everyone's implementation of your method. So, return something like this :
{
"id": "Some_ID",
"message": "Some Message",
"isAuthorized" : true
}
This way anyone who uses your API doesn't have to account for multiple arguments in the callback method. They know that they are only going to get one arg, and that it will be an Obj with multiple properties, and they can consume which properties they want. Further, in the future you can add a 4th or 5th property without breaking their code. You could add a "datetime" property, and their code would remain unchanged. You would get backwards compatibility, and new implementors could then use the new properties.
If I'm understanding you correctly, don't pass them as arguments at all:
var a = "something";
var b = "something";
var callback = function(){
// use a and b here
};
isAuthorized(socket, callback);
http://jsfiddle.net/S4tH6/
In Coffee-script you have something called splats.
( socket, callback, args... )->
callback args...
This translates as the following in Javascript:
var __slice = [].slice;
function() {
var args, callback, socket;
socket = arguments[0], callback = arguments[1], args = 3 <= arguments.length ?__slice.call(arguments, 2) : [];
return callback.apply(null, args);
};

Javascript convention for variable length arguments

I am getting more in to javascript development, and want to ensure I am following popular conventions.
Currently I have a library which consists of functions that can be passed either 1 model to operate on, or many models.
Given the climate that a few javascript libraries are very popular, I am curious; would I be conforming to the 'defacto standard' by achieving my 'single-item or list-of' requirement, by enumerating the arguments variable, or by allowing one of the arguments to be an array?
Scenario 1: argument enumeration
// passing a single entity to my function
sendMail( email, recipient1 );
// passing multiple entities to my function
sendMail( email, recipient1, recipient2 );
Scenario 2: entity argument is either single instance, or array
// pass a single entity
sendMail( email, recipient1 );
// passing multiple entities
sendMail( email, [recipient1, recipient2] );
I have seen areas of jQuery which use 'scenario 2', but I would still like to ask - which approach is the most popular, and why?
Thanks
[EDIT]
A couple of comments have followed the same vein, of using an arguments object - which is similar to 'scenario 2' - but I feel it introduces unnecessary complexity - the elements dont need to be named, because they are just a variable length list. I thought I would just add that here in case my question wasn't clear enough.
[EDIT]
I see code like this all through jQuery-1-7.js
queue: function( elem, type, data ) {
var q;
if ( elem ) {
type = ( type || "fx" ) + "queue";
q = jQuery._data( elem, type );
// Speed up dequeue by getting out quickly if this is just a lookup
if ( data ) {
if ( !q || jQuery.isArray(data) ) {
q = jQuery._data( elem, type, jQuery.makeArray(data) );
} else {
q.push( data );
}
}
return q || [];
}
}
[EDIT]
After some discussion with JP, I came up with this - which I'm not saying is the right choice, but it is very flexible...
lastArgumentAsParams: function()
{
var callerArgs = jQuery.makeArray(this.lastArgumentAsParams.caller.arguments);
// return empty set if caller has no arguments
if ( callerArgs.length == 0 )
return [];
callerArgs.splice(0, callerArgs.length - 1)
// remove all but the last argument
if ( callerArgs.length == 1 && jQuery.isArray(callerArgs[0]))
return callerArgs[0];
else
return callerArgs;
}
If you call this function at the beginning of any function - it will treat the last arg in the caller as a 'variable length argument' - supporting any of the conventions.
For example, I can use it like this
function sendEmail( body, recipients )
{
recipients = lastArgumentAsParams();
// foreach( recipient in recipients )...
}
Now, I can call 'sendEmail' in any of the following ways and it will work as expected
sendEmail('hello world', "bill#microsoft.com" );
sendEmail('hello world', "bill#microsoft.com", "steve#apple.com" );
sendEmail('hello world', ["bill#microsoft.com", "steve#apple.com"] );
I personally prefer using object literals for arguments to support named params, like this:
var myfunc = function(params){ //same as: function myfunc(params){....
alert(params.firstName);
alert(params.lastName);
};
myfunc({firstName: 'JP', lastName: 'Richardson'});
I think that it makes code very readable and order won't matter.
OR
You can also access the arguments object. Note, it's not an array, but it's "array-like". You can read about it here: http://javascriptweblog.wordpress.com/2011/01/18/javascripts-arguments-object-and-beyond/
Edit:
You seem to have a misunderstanding here. You're using the phrase "arguments object" and are thinking that it's the same as object literal notation. They are not.
The arguments object allows you to do this:
function myfunc(){
alert(arguments[0]); //JP
alert(arguments[1]); //Richardson
}
myfunc('JP', 'Richardson');
Does that help?
Another common way is to use object literal as variables:
myFunction(true, {option: value, option2: value});
I personally prefer this method for it is more verbose, and with javascript loose types, it gives you a better hint for what this variables is, and ignores order.
Backbone.js is using this as the preferred method.
To expand on the other answers, there are two main alternatives I usually see: optional arguments and keyword arguments. I don't remember seeing any good examples of the "array-using" idiom and it is kind of obsolete given how the arguments array is always available anyway.
Anyway, my rule of thumb is.
If I have many arguments, or the argument list is likely to change, or if the arguments don't have a good natural order, use the named arguments pattern
My favorite part about this style is that it is really flexible and future proof, while also being kind of self-documenting (in a smalltalk style).
foo({x1:'1', x2:'2', x3:'3'});
function foo(kwargs){
//I try to always copy the arguments back into variables.
//Its a little verbose but it helps documentation a lot and also
// lets me mutate the variables if I want to
var x1 = kwargs.x1,
x2 = kwargs.x2,
x3 = kwargs.x3;
}
If I have few arguments, that are not likely to change, and have a natural order to them, use a plain function (with the optional arguments last in the order)
foo(x1, x2);
foo(x1, x2, x3);
There are three main variations I can think right now of how to handle the optional arguments in the function:
var foo = function(x1, x2, x3){
//variation 1: truthy/falsy
// Short, but I tend to only use it when the variable stands
// for an object or other always-truthy kind of value
x3 = x3 || 'default_value';
//variation 2: using a special placeholder value for blank arguments.
// Usually this is null or undefined. (and undefined works if the arg is not passed too)
if(typeof x3 === 'undefined'){ x3 = 'default_value'; }
//variation 3: explicitly check the number of arguments
// I really like this one since it makes clear if the argument was passed or not.
if(arguments.length < 3){ x3 = 'default_value'; }
}
Also, there are so things I try to avoid:
Don't have functions that receive a large argument list. It can become a mess if they start becoming optional and you forget the order
foo(1, 2, null, null, 3, null, null); //ugh
Don't use fixed-length arrays to be tricky. They are redundant with no arrays at all and when I see an array I usually expect it to 1) be homogeneous and 2) be able to be as long as I want to
foo(true, [1, 2]); //should be foo(true, 1, 2)

Categories