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)
Related
Is it a bad practice to reuse function parameter variables?
const reformat = (phone)=>{
phone = phone.replace("-", ",")
//... more operation using phone
return phone;
}
is there any reason such reusage should be avoided? or is it always safe to continue such usage?
For strings, like in your example, it's fine. Some people may think it could get confusing if you're trying to debug and log that variable to the console sometime after it's been changed, but that's mostly subjective. Use your best judgement.
For objects, which are always passed by reference, you should avoid making changes in the function because those changes will still be present outside the function as well.
E.G.:
var myObject = {
message: "Hello, world"
};
function alertMessage(msgObj){
msgObj.message = "Hello moto";
alert(msgObj.message);
}
alertMessage(myObject);
// The object has changed.
console.log(myObject);
No it's not. And it's memory saving sometimes, since when you use another new variable, it will take up your memory. And the function process is much longer many transformations then it will be not a good practice to do. So according to your questions,
Yes, you can use the same variable inside the function (But make sure to consider what are the types that you refer when passing as arguments)
Re-usage should be avoided when you are using a object and that will change the content of the origin object.
different cases to take care:
// argument is not a reference ( string, numbers )
function add_5(num)
{
num += 5
return num
}
let initial_num = 10
let returned_num = add_5(initial_num )
console.log( `initial_num-> ${initial_num}, returned_num-> ${returned_num}`)
// argument is reference ( objects )
function directArgObj( obj)
{
obj.num += 5
}
let initial_obj = { num: 10, abc:'xyz' }
directArgObj(initial_obj )
console.log( `initial_obj-> ${JSON.stringify(initial_obj)} `)
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
I'm working my way through "Learning jQuery" (Third Edition).
In Chapter 4: "Manipulating the DOM" there is a section explaining something called the "Value Callback". This is a new one for me.
The author explains this via an example of list of links wherein the ID's of each must be unique.
From the book:
"A value callback is simply a function that is supplied instead of the value for an argument. This function is then invoked once per element in the matched set. Whatever data is returned from the function is used as the new value for the attribute. For example, we can use this technique to generate a different id value for each element, as follows:"
Chaffer, Jonathan (2011-09-23). Learning jQuery, Third Edition (p. 116). Packt Publishing. Kindle Edition.
jQuery(document).ready(function($){
// get all external links
$('div.chapter a').attr({
rel:'external',
title:'Learn more at Wikipedia',
id: function ( index, oldValue ) {
return 'wikilink-' + index;
}
});
})
Works like a charm, but the mechanics of the id: property escape me.
How does parameter 1 (index) know to be an integer?
How does the function know to increment index?
How does the second parameter (oldValue) know to hold the old value of the property (before modification)?
Is this a jQuery construct? A JSON thing? It's cool. it works, but ...what the heck is this "value callback" thing made of?
Please advise
1) How does parameter 1 (index) know to be an integer?
jQuery passes an integer.
2) How does the function know to increment index?
The callback doesn't increment index, the jQuery method does.
3) How does the second parameter (oldValue) know to hold the old value of the property (before modification)?
jQuery passes it.
The answers to questions 1-3 are perhaps best understood by a function that performs something similar to $.attr:
Array.prototype.each = function (f) {
var i;
for (i=0; i < this.length; ++i) {
f(i, this[i]);
}
};
['zero', 'one', 'two'].each(function (i,item) {console.log({i: item})});
f is a callback. each is responsible for iterating over a collection and calling f for each index & item. The same code structure can be used for functions:
/* Map each item in a sequence to something else,
* returning a new sequence of the new values.
*/
Array.prototype.map = function (f) {
var i, result = [];
for (i=0; i < this.length; ++i) {
result[i] = f(i, this[i]);
}
return result;
};
['zero', 'one', 'two'].map(function(i,item) {return item.length});
// result: [4, 3, 3]
/* Return a sequence of the items from this sequence
* for which 'keep' returns true.
*/
Array.prototype.filter = function (keep) {
var i, result = [];
for (i=0; i < this.length; ++i) {
if (keep(i, this[i])) {
result.push(this[i]);
}
}
return result;
};
['zero', 'one', 'two'].filter(function(i,item) {return item.length <= 3});
// result: ['one', 'two']
Implementation of mapconcat, foldl and foldr left as an exercise. As another exercise, rewrite map and filter in terms of each.
Note these functions are merely intended to illustrate how callbacks work. They may cause problems in production code.
4) Is this a jQuery construct? A JSON thing? It's cool. it works, but ...what the heck is this "value callback" thing made of?
Callbacks are a generic technique that jQuery makes extensive use of. They're the key feature of functional programming, where functions are data that can be operated on just like other data types. Thus, you have functions that take functions as arguments and can return functions. In certain contexts, callbacks are also known as "continuations" and form the basis of continuation passing style (CPS). This is particularly important for asynchronous function calls [2] (where the function returns before the computation completes, as opposed to synchronous calls), such as is used for Ajax requests. To see some of the power of CPS, read "Use continuations to develop complex Web applications".
The other aspect of this, the "value" in "value callback", is that, as JS is a dynamically typed language (types are associated with data, rather than variables), formal parameters can be bound to objects of any type. A function can then behave differently depending on what is passed. Sometimes this is implemented by examining the type of the argument, which is in effect ad-hoc polymorphism (the function, rather than the language, must handle dispatch). However, parametric polymorphism or (failing that) duck typing should always be preferred over examining argument types. Parametric polymorphism is achieved by ensuring that all types that can be passed to a given function support the same interface (method names, arguments, preconditions, postconditions & so on). For example, all sequence types should have a length property and be indexed by integers; as long as that holds, you can use your own sequence type with many functions that take arrays.
I'm not sure what you mean by JSON, but it's probably not what is generally meant. JSON is a data interchange format based on a limited version of the JS object literal syntax. JSON is not involved anywhere in the sample code or quoted text.
It's a JQuery construct. If you look at the source, you will find that JQuery is inspecting the parameter in order to learn whether you passed a value or a function. If it's a function, it handles as you see.
In Javascript I can call any method with more than the necessary amount of parameters and the extra parameters are silently ignored.
e.g.
letters = ['a','b','c']
//correct
letters.indexOf('a')
//This also works without error or warning
letters.indexOf('a', "blah", "ignore me", 38)
Are there ways to detect cases where this occurs?
My motivation is that in my experience cases where this occurs are usually bugs. Identification of these by code analysis or at runtime would help track these errors down.
These cases are especially prevalent where people are expecting alterations to base types which may not have occurred. Logging a warning where this happens
e.g.
Date.parse('02--12--2012', 'dd--MM--YYYY')
Notes:
To be clear I would like a solution that doesn't involve me sprinkling checks all over my code and other peoples' code.
You can use the arguments object.
function myFunction(param1,param2)
{
if (arguments.length!=2)
{
// wrong param number!
}
...
}
As per your edit: If you want to implement an automated form of check, without ever touching the original functions:
You still have to process each function with:
functionName = debug(functionName, numberOfExpectedArgs);
This operation wraps the function with a check of the number of arguments.
So we leave a sample function untouched:
// this is the original function... we want to implement argument number
// checking without insertint ANY debug code and ANY modification
function myFunction(a,b,c)
{
return a + " " + b + " " + c;
}
// the only addition is to do this...
myFunction = debug(myFunction,3); // <- implement arg number check on myFunction for 3 args
// let's test it...
console.log(myFunction(1,2,3));
console.log(myFunction(1,2));
You need to implement this debug() function:
function debug(f, n)
{
var f2 = f;
var fn = function()
{
if (arguments.length!=n) console.log("WARNING, wrong argument number");
return f2.apply(f2, arguments);
};
return fn;
}
This solution is TOTALLY transparent as per already defined functions, so it may be what you seek for.
I strongly suggest to check against deprecations (there are some) and crossbrowser compatibility.
The functions in JavaScript are objects. As such they have properties. What you want can be achieved with length MDN property, which specifies the number of arguments expected by the function.
function say ( hello, world ) {
alert (
"arguments length = " + arguments.length + "\n" +
"defined with = " + say.length
);
}
say ( "this ", "brave ", "new ", "world" );
This works even on IE8. Demo. In your case you can do something like this.
Javascript is a very dynamic language and many of its useful features also make it impossible to do some checks statically.
The existance of the arguments implicit object means there is no way to automatically determine how many arguments a function is expecting for all functions. Many var-arg functions declare no formal arguments and uses the arguments object exclusively.
All you can reliably do is to check it manually in each function like Cranio suggested.
If you want to have automated checks, e.g. as part of your unit tests, you can make use of the length property of the Function objects, which returns the number of formal arguments. For var-arg functions, just don't include the check. For example:
function checkNumberOfArguments(args) {
if (args.length != args.callee.length) {
throw new Error('Wrong number of arguments');
}
};
// Use it like
function a(b) {
checkNumberOfArguments(arguments);
}
a(1);
a(1,2);
Inside function you can use arguments object, it contains an array of all the arguments that were supplied to the function when it was called.
function x(){
return arguments.length;
}
x()
=> 0
x(1,1,1,1,1,1,1,1,1)
=> 9
Because jQuery is a widely used and mature collaborative effort, I can't help but to look at its source for guidance in writing better Javascript. I use the jQuery library all the time along with my PHP applications, but when I look under the hood of this rather sophisticated library I realize just how much I still don't understand about Javascript. Lo, I have a few questions for the SO community. First of all, consider the following code...
$('#element').attr('alt', 'Ivan is SUPER hungry! lolz');
vs
$('#element').attr({'alt': 'Ivan is an ugly monster! omfgz'});
Now, is this to say that the attr() method was designed to accept EITHER an attribute name, an attribute name and a value, or a pair-value map? Can someone give me a short explanation of what a map actually is and the important ways that it differs from an array in Javascript?
Moving on, the whole library is wrapped in this business...
(function(window, undefined) { /* jQuery */ })(window);
I get that the wrapped parentheses cause a behavior similar to body onLoad="function();", but what is this practice called and is it any different than using the onLoad event handler? Also, I can't make heads or tails of the (window) bit there at the end. What exactly is happening with the window object here?
Am I wrong in the assessment that objects are no different than functions in Javascript? Please correct me if I'm wrong on this but $() is the all encompassing jQuery object, but it looks just like a method. Here's another quick question with a code example...
$('#element').attr('alt', 'Adopt a Phantom Cougar from Your Local ASPCA');
... Should look something like this on the inside (maybe I'm wrong about this)...
function $(var element = null) {
if (element != null) {
function attr(var attribute = null, var value = null) {
/* stuff that does things */
}
}
}
Is this the standing procedure for defining objects and their child methods and properties in Javascript? Comparing Javascript to PHP, do you use a period . the same way you would use -> to retrieve a method from an object?
I apologize for this being a bit lengthy, but answers to these questions will reveal a great deal to me about jQuery and Javascript in general. Thanks!
1. Method overloading
$('#element').attr('alt', 'Ivan is SUPER hungry! lolz');
vs
$('#element').attr({'alt': 'Ivan is an ugly monster! omfgz'});
var attr = function (key, value) {
// is first argument an object / map ?
if (typeof key === "object") {
// for each key value pair
for (var k in key) {
// recursively call it.
attr(k, key[k]);
}
} else {
// do magic with key and value
}
}
2. Closures
(function(window, undefined) { /* jQuery */ })(window);
Is not used as an onload handler. It's simply creating new scope inside a function.
This means that var foo is a local variable rather then a global one. It's also creating a real undefined variable to use since Parameters that are not specified passes in undefined
This gaurds againts window.undefined = true which is valid / allowed.
the (window) bit there at the end. What exactly is happening with the window object here?
It's micro optimising window access by making it local. Local variable access is about 0.01% faster then global variable access
Am I wrong in the assessment that objects are no different than functions in Javascript?
Yes and no. All functions are objects. $() just returns a new jQuery object because internally it calls return new jQuery.fn.init();
3. Your snippet
function $(var element = null) {
Javascript does not support default parameter values or optional parameters. Standard practice to emulate this is as follows
function f(o) {
o != null || (o = "default");
}
Comparing Javascript to PHP, do you use a period . the same way you would use -> to retrieve a method from an object?
You can access properties on an object using foo.property or foo["property"] a property can be any type including functions / methods.
4. Miscellanous Questions hidden in your question
Can someone give me a short explanation of what a map actually is and the important ways that it differs from an array in Javascript?
An array is created using var a = [] it simply contains a list of key value pairs where all the keys are positive numbers. It also has all the Array methods. Arrays are also objects.
A map is just an object. An object is simply a bag of key value pairs. You assign some data under a key on the object. This data can be of any type.
For attr, if you give an object instead of a key value pair it will loop on each property.
Look for attr: in jQuery's code, then you'll see it use access. Then look for access: and you will see there is a check on the type of key if it is an object, start a loop.
The wrapping in a function, is to prevent all the code inside to be accessed from outside, and cause unwanted problems. The only parameters that are passed are window that allow to set globals and access the DOM. The undefined I guess it is to make the check on this special value quicker.
I read sometimes jQuery but I didn't start with it, may be you should get some good books to make you an idea first of what some advanced features Javascript has, and then apply your knowledge to the specifics of jQuery.
1 - Yes attr can accept a attribute name for getting a value, a name and a value for setting one value or a map of attribute names and values for settings more than one attribute
2 - A map is basically a JavaScript object e.g:
var map = {
'key1' : 'value1',
'key2' : 'value2'
};
3 - (function(window, undefined) { /* jQuery */ })(window); is something called an anonymous function as it doesn't have a name. In this case it also executes straight away.
A simple example would be:
function test(){
...
}
test();
//As an anonymous function it would be:
(function(){
...
}();
//And it you wanted to pass variables:
function test(abc){
...
}
test(abc);
//As an anonymous function it would be:
(function(abc){
...
}(abc);
this would make it different to the load event, as it is a function not an event.
4 - window is passed as a variable, as it is used internally within jQuery
5 - Objects and functions the same, as everything in JavaScript is an object. jQuery does something like this:
var obj = {
"init" : function(){
}
}
6 - Yes you can use . to retrieve a value on an object but you can also use [] e.g:
var map = {
"test" : 1
}
map.test //1
map["test"] //1
I hope this answers your many questions, let me know if I've missed anything out.
jQuery 1.6.1
The test is typeof key === "object"
if that is true, then you passed a { .... }
jQuery.fn.extend({
attr: function( name, value ) {
return jQuery.access( this, name, value, true, jQuery.attr );
},
// Mutifunctional method to get and set values to a collection
// The value/s can be optionally by executed if its a function
access: function( elems, key, value, exec, fn, pass ) {
var length = elems.length;
// Setting many attributes
if ( typeof key === "object" ) {
for ( var k in key ) {
jQuery.access( elems, k, key[k], exec, fn, value );
}
return elems;
}
// Setting one attribute
if ( value !== undefined ) {
// Optionally, function values get executed if exec is true
exec = !pass && exec && jQuery.isFunction(value);
for ( var i = 0; i < length; i++ ) {
fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
}
return elems;
}
// Getting an attribute
return length ? fn( elems[0], key ) : undefined;
},