understanding the object passed in two contexts - javascript

I don't know if you've ever felt like you've dived off the stupid tree and hit every branch on the way down, but JavaScript has that effect on me, an experienced PHP programmer but just doesn't get JS sometimes.
so I wrote this function using jQuery.extend({ .. }) as follows:
loadSection: function(obj){
if(typeof obj=='undefined')obj=event.target; //but this doesn't work
if(typeof obj=='string')obj=document.getElementById(obj);
var params=null;
var instr={};
var k=0;
while(true){
..etc..
I want to be able to call it two ways:
$.loadSection($('#employee-data-173'));
//or $loadSection('employee-data-173') for convenience
or:
$('#employee-data-173').loadSection();
Clearly (to you anyway!) I'm not grasping the concept of what's being passed in the second case. And that's not even getting into a case like this:
$('.someElement').click(loadSection);

You want to use the same function in three totally different usecases. In each case different arguments are automatically passed to the function. So first declare and prepare it to handle all possible types of arguments:
function loadSection(index, obj, args) {
var element, params = null;
if (obj && obj.nodeType) { // for usecase 2
element = obj; if (typeof args == 'object') params = args;
} else {
if (typeof obj == 'object') params = obj;
if (typeof index == 'string') { // usecase 1 with selector-string
element = document.getElementById(index);
else if (index.jquery) { // usecase 1 with jQuery object
if (index.length == 1) element = index[0]; // if only one element inside jQuery
// if more than one element there call this function recursively on each
else index.each(loadSection);
}
else if (index.target) element = index.target; // for usecase 3
}
/* your stuff */
}
In your usecase 1 you have to add the function to the global jQuery object and call it by $.loadSection(argument), where argument may be a id-selector-string or an jQuery-object $("selector"). There are two ways with identic result:
$.loadSection = loadSection;
$.extend({loadSection: loadSection});
In usecase 2 you want to call the function as a method of a jQuery object. Therefore you have to add it to the jQuery.prototype like so:
$.fn.loadSection = function( args ) { // $.fn is synonym for jQuery.prototype
return this.each(loadSection, args);
};
If you call now $("selector").loadSection() the function is executed once for each element matched by "selector". Arguments index and obj are automatically passed to loadSection.
In usecase 3 the function is used as callback-function for an event. Since its prepared for this case, the event object is automatically passed to it. So just do:
$('.someElement').click(loadSection);
You can use all cases mixed in the same piece of code.
EDIT "What shall/can the function return?"
It may return whatever you want to. Only the behaviour depends on usecase.
In usecase 1 you can do: var result = $.loadSection("selector") and result gets the returned value.
In usecase 2 there is an iteration over all elements in the jQuery object. The return value of loadSection is simply ignored except you explicitely return false. Then iteration stops. So if you do if (index == 2) return false; the function is executed maximum 3 times even when you have 7 elements inside jQuery object.
The function as a whole always returns the jQuery object, so you can do chaining:
$("selector").loadSection().css({color: 'blue'}).animate(...). ...
In usecase 3 the function is only executed but the return value gets never recognized anywhere, so you can't catch it.
EDIT 2 "How to pass additional params to the function?"
1) Now loadSection is prepared to take additional args (see above). 2) The setup of $.fn.loadSection is modified to take args (see above). 3) args must be an object or array eg {prop: 'color', id: 23} (otherwise it's ignored) but may be omitted.
In usecase 1 pass args as second argument
var result = $.loadSection("selector", args); // you find args inside in ""var params"
In usecase 2 args is the one and only possible argument. jQuery now makes an iteration inside an iteration: the function is called on each element once for each item in args!
The inner iteration is stoppable by return false, but the iteration over all elements no longer.
$("selector").loadSection({prop: 'color', id: 23}) // if $() contains 3 elems, function run six times
In usecase 3 its impossible to pass args since you only point to the function by its name.

Related

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

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

How does Function.bind.bind(Function.call) uncurry?

We have this line in my code base:
var uncurryThis = Function.bind.bind(Function.call);
That I'm trying to work through. Presumably, it uncurries. How do I work this out?
I guess it's a version of Function.bind whose own this is bound to Function.call. Doesn't help me enough. And I haven't found any uses, so I'm not even sure if you call it standalone or need to call it "as a method", only, you know, bind it first.
It passes the call function to the bind function, with the bind function itself being the value of this. Thus you get in return a wrapper around the bind function that arranges for this to be the call function when you call it. That, in turn, is a function that lets you create a wrapper around the call function bound to some argument you pass it.
In case you haven't been drinking coffee nonstop since you woke up this morning, step by step:
Function.bind.bind is a reference to the bind function. The reference is generated from a property of — confusion point 1 — the bind function itself. Remember, the bind function, when called with some function as the object, is used to create a wrapper around that function with this bound to the first argument passed in.
Thus that function call gives you a function back. That function works as if you called Function.call.bind(something).
If you pass some random function as an argument to that function, then, you get back a wrapper around the random function that, when called, will act like randomFunction.call(whatever).
So:
function random() {
alert(this.foo);
}
var bb = Function.bind.bind(Function.call);
var randomcall = bb(random);
randomcall({ foo: "hello world" }); // alerts "hello world"
The ultimate point is this: you've got a function, and inside the function there's code that expects this to have some properties, and it uses this in one way or another. You'd really like to be able to use that function with some object here, some object there. You can obviously do that with
random.call(someObject);
But this magic "bind-bind-call" trick gives you a cheap way to create a variation on your function that lets you avoid the explicitly-coded invocation of .call(). It also allows you to hang onto your senior front-end developer position for a little bit longer.
edit — I'm going to spoil the punch line above because I just thought of a good reason to use the bind+call trick to obtain a function that arranges to make a call to some desired function that expects to operate via this on some "owner" object. Let's say you've got an array of strings, and you'd like to get a version of those strings in lower-case. You could write this:
var uc = ["Hello", "World"];
var lc = uc.map(function(s) { return s.toLowerCase(); });
But with the magic "bb" function we could also write:
var uc = ["Hello", "World"];
var tlc = bb(String.prototype.toLowerCase);
var lc = uc.map(tlc);
Not much of an improvement written that way, but if one were to make a set of bb()-ified wrappers of all the handy String prototype methods, it might make more sense. Of course, everything has a price, and it's probably the case that such wrappers will have some performance impact. (If practices like this were common then runtimes could probably be improved.)
OK. You know what bind does? It's a method of Functions to fix their this argument, and returns a new function. It could be simplified to:
function bind(context) {
var fn = this;
return function() {
return fn.apply(context, arguments);
};
}
I will abbreviate function calls with contexts in a more functional style with lots of partial application: bindfn(context) -> fncontext. With arguments: (bindfn(context))(…) is equal to fncontext(…).
Similarly, call does take a this value but instead of returning a function, it applies it right now: callfn(context, …) -> fncontext(…).
So now let's get at your code: bind.call(bind, call). Here, you're applying bind on bind with call as the this value: bindbind(call). Let's expand this (with above rule) to bindcall. What if we now supplied some arguments to it?
bindbind(call) (fn)(context, …)
bindcall (fn)(context, …)
call fn(context, …)
fncontext(…)
Step by step, we could do
uncurryThis = bindbind(call) // bindcall
func = uncurryThis(method) // callmethod
result = func(context, …) // methodcontext(…)
A practical use case for this are any "class" methods that are supposed to be converted to a static function, taking the object (on which the method would be called upon) as the first argument:
var uncurryThis = Function.bind.bind(Function.call);
var uc = uncurryThis(String.prototype.toUpperCase);
uc("hello") // in contrast to "hello".toUpperCase()
This can be helpful if you cannot place a method call, but need a static function; e.g. as in
["hello", "world"].map(uc) // imagine the necessary function expression
Also, the method you want to invoke might not be a method of the object itself, as in
var slice = uncurryThis(Array.prototype.slice);
slice(arguments) // instead of `Array.prototype.slice.call(arguments)` everywhere
If it helps, here is also an explicit implementation, without any binds:
function uncurryThis(method) {
return function(context/*, ...*/)
return method.apply(context, Array.prototype.slice.call(arguments, 1));
};
}
when we call bind on a function, it returns new function with this is replaced by context:
function random() {
alert(this.foo);
}
var newRandom = random.bind({foo:"hello world"}) //return new function same as //`random` with `this` is replaced by object {foo:"hello world"}
the same we have:
Function.bind.bind(Function.call)
// return new Function.bind with its `this` is replaced by `Function.call`
It has following source(used simplified version of bind function given by #Bergi):
var bb = function bind(context){
var fn = Function.call;
return function() {
return Function.call.apply(context, arguments); //also replace fn here for easier reading
};
}
Notice that context here will be function, for example random, so wen call bb(random) we has newRandom function as:
newRandom = function(){
return Function.call.apply(random, arguments); //also replace
}
//`apply` function replace `this` of Function.call to `random`, and apply Function(now become `random`) with arguments in `arguments` array.
I think this can be explained more clearly if you work backward.
Context:
Suppose we want to lowercase an array of strings. This can be done like so:
[‘A’, ‘B’].map(s => s.toLowerCase())
Let's say, for whatever reason, I want to make this call more generic. I don't like how s is bound to this and the fat arrow is tied to toLowerCase().
How about this?
[‘A’, ‘B’].map(String.prototype.toLowerCase)
Well, this doesn't work because map passes the element as the first argument but String.prototype.toLowerCase takes no arguments. It expects the input string to be passed as this.
So a question is can we create a wrapper function that makes this work?
[‘A’, ‘B’].map(wrapper(String.prototype.toLowerCase))
wrapper returns a function that turns the first argument passed into this for String.prototype.toLowerCase to use.
I claim that your uncurryThis === wrapper.
Proof:
So let's not try to understand unCurryThis all at once. Instead, let's use some formulas to transform unCurryThis into something more understandable.
Some formulas first:
instance.function(...args)
=== (instance.constructor.prototype).function.call(instance, ...args)
=== (Class.prototype).function.call(instance, ...args) [1]
=== (Class.prototype).function.bind(instance)(...args) [2]
For example,
Class === String
instance === 'STRING'
function === toLowerCase
args === []
---
'string'.toLowerCase()
=== ('STRING'.constructor.prototype).toLowerCase.call('STRING')
=== (String.prototype).toLowerCase.call('STRING')
=== (String.prototype).toLowerCase.bind('STRING')()
So let's just blindly apply these formulas without worrying about what the confusing uncurryThis looks like:
'string'
=== (wrapper)(String.prototype.toLowerCase)('STRING')
=== (uncurryThis)(String.prototype.toLowerCase)('STRING')
=== (Function.bind.bind(Function.call))(String.prototype.toLowerCase)('STRING')
// Function.bind is not really the generic form because it's not using the prototype
// Here Function is an instance of a Function and not the constructor.prototype
// It is similar to calling Array.bind or someFunction.bind
// a more correct version would be
// someFunction.constructor.prototype.bind === Function.prototype.bind, so
=== (Function.prototype.bind.bind(Function.prototype.call))(String.prototype.toLowerCase)('STRING')
// Apply formula 2
// instance.function(...args) === (Class.prototype).function.bind(instance)(...args) [2]
// Class === Function
// function === bind
// instance === Function.prototype.call
// ...args === String.prototype.toLowerCase
=== instance.function(...args)('STRING')
=== (Function.prototype.call).bind(String.prototype.toLowerCase)('STRING')
// Apply formula 2 again
// Class == Function
// function == call
// instance === String.prototype.toLowerCase
// ...args === 'STRING'
=== instance.function(...args)
=== (String.prototype.toLowerCase).call('STRING')
// Apply formula 1
instance.function(...args) === (Class.prototype).function.call(instance, ...args) [1]
// Class === String
// function === toLowerCase
// instance === 'STRING'
// args === []
=== instance.function(...args)
=== 'STRING'.toLowerCase(...[])
=== 'STRING'.toLowerCase()
// So we have
(wrapper)(String.prototype.toLowerCase)('STRING')
=== (uncurryThis)(String.prototype.toLowerCase)('STRING')
=== 'STRING'.toLowerCase()
=== 'string'
Reverse Proof:
So you might wonder "how did the guy even derive the uncurryThis function"?
You can reverse the proof to derive it. I am just copying the equations from above but reversed:
'STRING'.toLowerCase()
=== (String.prototype.toLowerCase).call('STRING') // apply formula [1]
=== (Function.prototype.call).bind(String.prototype.toLowerCase)('STRING') // apply formula [2]
// At this point, you might wonder why `uncurryThis !== (Function.prototype.call).bind)
// since it also takes (String.prototype.toLowerCase)('STRING')
// This is because passing in (Function.prototype.call).bind) as an argument
// is the same as passing in Function.prototype.bind
// `this` binding isn't done unless you call
// (Function.prototype.call).bind)(String.prototype.toLowerCase)
// at that exact moment.
// If you want to be able to pass unCurryThis as a function, you need to bind the
// Function.prototype.call to the Function.prototype.bind.
=== (Function.prototype.bind.bind(Function.prototype.call))(String.prototype.toLowerCase)('STRING') // apply formula 2
=== (Function.bind.bind(Function.call))(String.prototype.toLowerCase)('STRING') // un-generic-ize
=== (uncurryThis)(String.prototype.toLowerCase)('STRING')
=== (wrapper)(String.prototype.toLowerCase)('STRING')
=>
unCurryThis === wrapper === Function.bind.bind(Function.call)
Still pretty confusing to follow but try to write out what Class, function, instance, and args are each time I am applying formulas [1] and [2], and it should make sense.

No concept of overloading in JavaScript

Check this fiddle or the code below:
function abc(s) {
console.log('in abc(s)');
}
function abc(s, t) {
console.log('in abc(s,t)');
}
abc('1');
The output of this question is always in abc(s,t)
Can someone please explain me whats going on here and why ?
In Javascript there is no overload concept.
You can however write a function that checks how many arguments have been passed by using the arguments value.
function foo(s, t) {
if (arguments.length == 2) {
...
} else {
...
}
}
all arguments that the function expects in the signature but that are not passed by the caller are received as undefined. You can also write variadic functions by simply accessing the n-th argument passed with arguments[i]. Note however that arguments is not a Javascript array, so not all array methods are available for it.
About being able to redefine the same function multiple times without errors things are a bit complex to explain because the rules are strange.
A simple explanation is you could think of is that function is an executable statement like it is in Python and so the last function definition wins. This would be wrong however because, differently from Python, the following is legal Javascript code:
console.log(square(12));
function square(x) { return x*x; }
i.e. you can call a function in lines that are preceding the definition (in a script: of course typing those two lines in a Javascript console wouldn't work).
A slightly more correct explanation is that the compiler first parses all the function definitions (last wins) and then starts executing the code. This mental model works if you don't put functions inside if because what happens in practice in that case is implementation dependent (and I'm not talking about crazy IE, but even that FF and Chrome will do different things). Just don't do that.
You can even use the form
var square = function(x) { return x*x; }
and in this case it's a simple assignment of a "function expression" to a variable that is executed when the flow passed through it (so it's ok to place different implementations of a function inside different if branches, but you cannot call the function before assigning it an implementation).
First, no method overload support in JavaScript (see #6502 workaround).
Second, to describe what you're experiencing, in JavaScript, the last declared function (with the same name) is invoked because the former has been overwritten, It relates to JavaScript Hoisting.
Try to reorder the functions declarations and see the output result again:
function abc(s, t) {
console.log('in abc(s,t)');
}
function abc(s) {
console.log('in abc(s)');
}
abc('1');
In javascript, there is only one function with any given name and if multiple functions with the same name are declared, the last one declared will be the one that is active.
You can however test the arguments that are passed to your function and implement many of the same types of behaviors that function overloading is designed to handle. In fact, in some cases you can do even more.
In your specific example:
function abc(s, t) {
// test to see if the t argument was passed
if (t !== undefined) {
console.log('was called as abc(s,t)');
} else {
console.log('was called as abc(s)');
}
}
abc('1'); // outputs 'was called as abc(s)'
abc('1', '2'); // outputs 'was called as abc(s,t)'
But, you can also get much, much more creative (and useful).
For example, the jQuery .css() method can be called five different ways.
.css( propertyName )
.css( propertyNames )
.css( propertyName, value )
.css( propertyName, function(index, value) )
.css( properties )
The code inside the .css() method examines the type and number of the arguments to figure out which way it is being called and therefore exactly what operation to carry out.
Let's look at how this could be done to figure out which of the 5 forms of this function are being used:
css: function(prop, value) {
// first figure out if we only have one argument
if (value === undefined) {
if (typeof prop === "string") {
// we have a simple request for a single css property by string name
// of this form: .css( propertyName )
} else if (Array.isArray(prop)) {
// we have a request for an array of properties
// of this form: .css( propertyNames )
} else if (typeof prop === "object") {
// property-value pairs of css to set
// of this form: .css( properties )
}
} else {
if (typeof value === "function") {
// of this form: .css( propertyName, function(index, value) )
} else {
// of this form: .css( propertyName, value )
}
}
}
You can also implement optional arguments. For example, jQuery's .hide() can accept many forms. One of the forms is .hide( [duration ] [, complete ] ) where both the duration and the completion function are optional. You can pass nothing, just a duration or both a duration and completion callback function. That could be implemented like this:
hide: function(duration, fn) {
// default the duration to zero if not present
duration = duration || 0;
// default the completion function to a dummy function if not present
fn = fn || function() {};
// now the code can proceed knowing that there are valid arguments for both
// duration and fn whether they were originally passed or not
}
I find one of the most useful ways of using these variable arguments are to allow code to support a variety of different argument types so that no matter what state your arguments are in, you can just pass them as you have them without having to convert them to some universal type. For example, in this implementation of a set object in javascript, the .add() method can take all of these different forms of arguments:
s.add(key)
s.add(key1, key2, key3)
s.add([key1, key2, key3])
s.add(key1, [key8, key9], key2, [key4, key5])
s.add(otherSet) // any other set object
s.add(arrayLikeObject) // such as an HTMLCollection or nodeList
This both accepts a variable number of arguments and it accepts a number of different types for each argument and it will adapt based on what is passed to it. So, you can initialize a set via a list of keys, an array of keys, from another set, from a pseudo array or any mixture of those types. Internally, the code just iterates through each argument that was passed to the function, checks the type of the argument and acts accordingly.
You can see the code here on GitHub for further info on how this is done.

Setting a JavaScript function as a parameter for use with another function?

I'm learning lots of javascript these days, and one of the things I'm not quite understanding is passing functions as parameters to other functions. I get the concept of doing such things, but I myself can't come up with any situations where this would be ideal.
My question is:
When do you want to have your javascript functions take another function as a parameter? Why not just assign a variable to that function's return value and pass that variable to the function like so:
// Why not do this
var foo = doStuff(params);
callerFunction(foo);
//instead of this
callerFunction(doStuff);
I'm confused as to why I would ever choose to do things as in my second example.
Why would you do this? What are some use cases?
Here's yet another example. Does some formatting operations on an array:
function pctFormatter(num) {
return num + '%';
}
function centsFormatter(num) {
return num + '.00';
}
function formatThisArray(array, formatter) {
var output = [];
for(var i = 0; i < array.length; i++) {
output.push( formatter(array[i]) );
}
return output;
}
formatThisArray([1,2,3], pctFormatter);// returns ['1%', '2%', '3%']
formatThisArray([1,2,3], centsFormatter);// returns ['1.00', '2.00', '3.00']
Handlers/listeners are a good example.
More generally, you can pass a function f as a parameter to function g when you don't know yet if g will need to call f, how many times it will need to call it, and/or with which parameters.
Examples:
sort algorithms: comparison function
regular expressions: replace function
callbacks (e.g. event handlers)
You'd do it when you don't have the params to pass, but the callerFunction() does.
A callback to an AJAX request is one use case.
function myCallback(response) {
// do something with the response
}
myAJAX('http://example.com/foo.json', myCallback)
This lets myAJAX to the work of making the request, and waiting for the response. Then it invokes myCallback and passes it the response when that response finally arrives.
// Why not do this
var foo = doStuff(params);
callerFunction(foo);
//instead of this
callerFunction(doStuff);
First example will run the function doStuff with params and the assign the result to foo. callerFunction will be called with parameter foo (which is now a result of dooStuff);
Second example will call callerFunction and pass doStuff as a parameter. The callerFunction might or might not call the doStuff.
Well, sometimes you don't know who the caller of a function will be until it's called - this precludes passing pre-calculated values.
A couple of examples that spring to mind are:
(a) setTimeout or setInterval - you want to call a specific function after a specified period, either one-shot, or repeatedly. If the function called returned a value that had a dependance on time, there are instances where you couldn't possibly pre-calculate the value - it needs to be done at the scheduled time. So, we tell the functions which of our own functions to call at the specified time.
(b) when loading (or at least attepmpting to) various resources. We can give the element a function that is to be executed when loading is successful, and another when it fails. You don't actually know when the effort to load a resource has finished until either of these two (user-supplied) functions are called. In the case of many resources, this is where you increment the counters that maintain the number of successful/failed load attempts.
(c) the NodeList returned by calls to getElementsByClass or getElementsByTagName. It's not an actual (javascript native) Array object. As such, you can't call the forEach method on it, like you can with an array. To get around this, I use the following helper function:
// getElementsByTagName, getElementsByClass - both return a NodeList
// it is accessed in the same way as an array - with the [] operators, but it's
// not an array object - this is a function that allows us to still iterate through it
// in much the same way.
function forEachNode(nodeList, func)
{
var i, n = nodeList.length;
for (i=0; i<n; i++)
{
func(nodeList[i], i, nodeList);
}
}
This allows me to get a list of nodes and then call some user-defined function on each of them. In use, it looks like this:
var allAnchors = document.getElementsByTagName('a');
forEachNode(allAnchors, showNodeTextVal);
function showNodeTextVal(curElem, curIndex, origList)
{
alert(curElem.innerText);
}
Or more simply:
var allAnchors = document.getElementsByTagName('a');
forEachNode(allAnchors, function(curElem){alert(curElem.innerText);} );
This is a much clearer, less error-prone situation than it would be if we didn't use this helper function. To achieve the same functionality, we'd need to code the following:
var nodeList = document.getElementsByTagName('a');
var i, n = nodeList.length;
for (i=0; i<n; i++)
{
alert(nodeList[i].innerText);
}
Most common case is handlers in JQuery:
function clickHandler(e){
// handle click on e.Target
}
$("#button").click(clickHandler);
$(function(){
// do ready state initialization
});
callerFunction(doStuff);
with this code you give a "pointer" of the function doStuff to the function callerFunction
you can use it like this:
function callerFunction(doStuff) {
var x = doStuff(...);
...;
}
you can so use the function in the function and not only the return value of doStuff.
greetings!
When do you want to have your javascript functions take another
function as a parameter?
It's useful for callbacks for example:
function add( a, b, callback ) {
callback( a, b );
return a + b;
}
function added( a, b ) {
alert('You just added two numbers: '+ a +' and '+ b);
}
alert( add( 1, 2, added ); // Will alert the message and then the result.
This a very simple example but it's very useful with asynchronous functions so you can run code after it has finished without interrupting the script.
You need to pass functions themselves, not return values, when you want to have your code really deal with functions as functions - code to execute. Consider this pseudo-code example:
function saveToLocalStorage(data) {...//saves to local storage}
function saveToServer(data) {...//saves via AJAX to server}
function saveToAmazonS3(data) {.../saves to Amazon S3 }
function multiSave(data, saverFunctions) {
saverFunctions.forEach(function (saverFunction) {
saverFunction(data);
});
}
multiSave({user: "tim"}, [saveToLocalStorage, saveToServer, saveToAmazonS3]);
In this case, I want the actual functions themselves to be passed around and for other code to later invoke them. When we do this, a function such as multiSave is called a higher-order function because it deals with other functions directly. Because of the way multiSave works, I can easily put some checkboxes in the UI next to local/server/S3 and allow the user to choose where their data goes in a way that would be less elegant if I was unable to pass functions around as arguments.
When you're passing a function as an argument, that argument is not the return value of that function, but it's the function itself, you can call it as much as you like, with any argument you like, or you can assign it to an event. You say you want some practical use cases, here's a short list of very common situations, all requiring a function to be passed as an argument.
Let's take a look at your average jQuery code, and count the number of times where a function is passed as an argument:
$(document).ready(function()//<-- 1
{
$('#foo').on('click',function()//2
{
});
$.each(something,function()//3
{});
//and so on
});
If you don't use jQuery, then try event delegation
document.body.addEventListener('click',function(e)
{
e = e || window.event
console.log('This function was passed as an argument to the addEventListener method');
},false);
Or even the simple Array.prototype.sort function (/method):
anArray.sort(function(a,b)
{
return (a > b ? 1 : -1);
});
Or in cases where you need to make an ajax call, instead of creating a new XMLHttpRequest object on the spot, you might want a single function that sets the xhr object up, and pass the url, data and onreadystatechange callback as arguments:
function makeXHR(url,data,callback)
{
try
{
var xhr = new XMLHttpRequest();
}
catch(e)
{
//etc...
}
xhr.onreadystatechange = callback;
}
makeXHR('some/url','foo=bar',function()
{
if (this.readyState === 4 && this.status === 200)
{
//do stuff
}
});
In all of these examples, I've created the functions in-line, of course referencing a function (by just passing its name) works just fine, too:
makeXHR('some/url','foo=bar',defaultXhrCallback);
These are just a few of thousands of use cases where you can/have to pass a function as an argument to another function

Better Understanding Javascript by Examining jQuery Elements

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;
},

Categories