What is the difference between Function.caller.arguments and Function.arguments? - javascript

Is there a difference whether I use foo.caller.arguments or simple arguments inside function foo:
function foo(){
console.log(foo.caller.arguments);
}
function foo(){
console.log(arguments);
}

As it's said in the comments to the question, caller is not a standard property:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller
Having said that, it returns a reference to the caller, that is, the function that has called the current function. So, caller.arguments gets you the arguments with which the caller has been invoked.
arguments gets you the arguments used in the call to the current function.
For example:
function one(c) {
console.log('arguments', arguments);
console.log('caller.arguments', one.caller.arguments);
}
function two(a, b) {
one(3);
}
two(1, 2)
Prints
arguments [3]
caller.arguments [1, 2]

arguments gives the arguments to the fuction itself whereas caller.arguments gives the arguments of the functions which calls this function. Following code will give you the basic understanding.
BTW function.caller is Non-standard as suggested bt MDN
var foo = function(name) {
bar('second');
}
var bar = function(surname) {
console.log(arguments);
console.log(bar.caller.arguments)
}
foo('first');

Related

What is fn.bind.apply(fn, arguments) doing?

I saw this shortcut given as an answer on a code Kata but I am having difficulty understanding exactly what the below example is doing.
function func(fn) {
return fn.bind.apply(fn, arguments);
}
So far my understanding is that bind creates a new function similar to doing the following:
function func(fn) {
return function () {
return fn.apply(fn, arguments);
};
}
Is this the case? Any clearer answers or breakdowns of what is going on would be great.
fn.bind
is just
Function.prototype.bind
So we're applying bind to fn, returning
fn.bind(arguments[0]/* doesn't matter, it's fn*/, arguments[1], arguments[2], etc.)
So the bound function is called with arguments being the arguments of func after fn.
Another way to write it would have been:
function func(fn) {
var args = [].slice.call(arguments, 1);
return function () {
var localArgs = [].slice.call(arguments);
return fn.apply(fn, args.concat(localArgs));
};
}
The fact that the context of the call is the initial function (arguments[0]) is most certainly only a side effect. The important thing is we wrap the arguments with the function, but make it possible to dynamically pass other arguments.
Example 1, wrapping all arguments :
function add(a,b){
return a+b
}
var f = func(add, 2 ,3); // f is a function which will always apply add to 2 and 3
console.log(f()) // logs 5
Exemple 2, currying:
function multiply(a,b){
return a*b
}
var multBy2 = func(multiply, 2);
console.log(multBy2(3)) // logs 6

Modifying Array.forEach to Automatically Set the Context to be the Caller

Is it possible to create an alternate of Array.forEach that automatically sets the context "this" to be the same context as when the method was invoked?
For example (not working, not sure why):
Array.prototype.each = function(fn) {
return this.forEach(fn, arguments.callee.caller);
}
function myFunction() {
this.myVar = 'myVar';
[1,2,3].each(function() {
console.log(this.myVar); // logs 'myVar'
});
}
Array.forEach already takes a context argument as the optional last parameter,
(function() {
this.myvar = "myvar";
[1,2,3,4].forEach(function(v) {
console.log("v:"+v);
console.log("myvar="+this.myvar);
}, this);
})();
See MDN forEach
Also, the above examples (if we're not dealing with methods on instances regarding this) work without using bind or the optional context argument for forEach, the following also works correctly:
function myFunction() {
this.myVar = 'myVar';
[1,2,3].forEach(function() {
console.log(this.myVar); // logs 'myVar'
});
}
myFunction();
Because javascript is functionally scoped, so the anonymous function can access the parent function's scope using this and it logs correctly. this only really becomes problematic as a context when dealing with instance methods.
The answer is no, a JavaScript function cannot determine the value of this in the caller.
You can bind the function passed with the current object, like this
function myFunction() {
this.myVar = 'myVar';
[1,2,3].forEach(function() {
console.log(this.myVar); // logs 'myVar'
}.bind(this));
}
In ECMA Script 6, you can use an Arrow function, like this
[1,2,3].forEach(() => {
console.log(this.myVar); // logs 'myVar'
});
An alternative to messing with the this variable when passing around callbacks, you could always just assign this to a new variable so child scoped functions can access it:
Array.prototype.each = function(fn) {
return this.forEach(fn, arguments.callee.caller);
}
function myFunction() {
var me = this;
me.myVar = 'myVar';
[1,2,3].each(function() {
console.log(me.myVar); // logs 'myVar'
});
}
now you don't have to remember to pass this as a second parameter
Firstly, it must be pointed out that myFunction is a constructor. Yet, the first letter in the identifier is not capitalized. Please call it MyFunction.
If a constructor is called without the new operator, this is bound to the global object, i.e. window in browsers. This makes the capitalization convention our only way of spotting such mishaps.
The following lines of code demonstrate this:
// After the original code...
myFunction();
console.log(window.myVar); // logs "myVar"
Secondly, to be able to apply functions on any array, instead of changing Array.prototype, consider the following:
var utils = {array: {}}; // utils.array is a container for array utilities.
utils.array.each = function (array, func) {
var i;
for (i = 0; i < array.length; i += 1) { func(array[i]); }
};
utils.write = function (s) {
console.log(s); // Alternatively, document.write(s);
};
utils.array.each([1, 2, 3], utils.write); // prints 1 2 and 3 (on separate lines)
Notice that we didn't use this and new. They make JavaScript look like Java, apart from that, they rarely serve a useful purpose.
While libraries may modify Object.prototype and Array.prototype, end-developers shouldn't.
Also, we should (ideally) be able to do something like:
utils.array.each([1, 2, 3], console.log); or
utils.array.each([1, 2, 3], document.write);.
But most browsers won't allow it.
Hope this helped.
If I understand your requirement correctly, then you are trying to override the "this".
I think this can help you.

Where do function arguments exist when using .call()/.apply()?

i came across a situation where I need to call another function with .call() or .apply() like this:
function b() {
alert(arg);
}
Then
function a(arg) {
b.call();
}
a(123);
Function b is called, but doesnt' have access to arg. That's ok, I can pass scope.. yes?
function a(arg) {
b.call(this);
}
a(123);
Still no - I can't access arg from function b. How can I do it?
UPDATE:
I do not want to modify b function :-)
You still need to pass the arguments via call (individually) or apply (as an array):
function a(arg1, arg2, arg3) {
b.call(this, arg1, arg2, arg3);
// or
b.apply(this, arguments)
// or
b.apply(this, [arg1, arg2, arg3]);
}
Of course, nothing about your situation suggests actually using call or apply: Just invoke the function yourself.
function a(arg) {
b(arg);
}
It’s not possible to “pass scope” or something like that. The scope of a function is determined when the function is created, so it can only access arg if it exists where b is defined.
If arg was part of this, then you could do this using call, i.e. make the this in b the same this as it is in a (of course this will modify whatever this actually refers to, which can have side effects you might not want to happen).
function a (arg) {
this.arg = arg;
b.call(this);
}
function b () {
console.log(this.arg);
}
The other way would be to just pass the argument to b as an actual function argument. You can access all arguments of a function using arguments:
function b () {
console.log(arguments);
}
Try this one:
function a(arg) {
b.apply(this, [arg]);
// or
// b.call(this, arg);
}
function b() {
alert(arguments);
}
You failed to pass the arguments when you called b.
Function::call allows you to pass a fixed number of arguments:
function a(arg1,arg2) {
return b.call(this,arg1,arg2);
}
Function::apply allows you to pass any number of arguments, as an array:
function a(arg1,arg2) {
return b.apply(this,[arg1,arg2]);
}
// or
function a(arg1,arg2) {
return b.apply(this,arguments); // The magical 'arguments' variable
}
this is the context object, and is not the same thing as scope.
I'll guess the problem lies in b(). There's no 'arg' argument defined. Try:
function b(arg) { alert(arg); }
and
function a(arg) {
b.call(this,arg);
}
a(123);
now it runs
(Update: the call needs the arguments ( context functionarg1, functionarg2...) )
Assuming that, for some reasons you can't modify the body of b , you can try something like this:
function a(arg) {
eval("("+ b.toString() +")()");
}
DEMO

How do I pass a function(delegate?) as a parameter to another function in Javascript and then use it

I want to pass a function to another function. I think functions being passed like that are call delegates? I am having a hard time finding a good explanation for this kind of thing online. Is this the right way to do this?
function getCellContentByColumnIndex = function(row, index) {
return $(row.children().get(index)).text();
}
function naturalSort(a, b, func) {
//...
var $a = func(a);
var $b = func(b);
//...
}
//usage
naturalSort(x, y, getCellContentByColumnIndex);
Your code:
function getCellContentByColumnIndex = function(row, index) {
return $(row.children().get(index)).text();
}
Is a syntax error. The following is a function declaration:
functon foo() {}
And this is a function expression:
var foo = function(){}
And this is a named function expression:
var foo = function bar(){}
There are a number of answers here on the differences, there is a detailed explanation in the article Named function expressions demystified which also covers many other aspects of function declarations and expressions.
The term "anonymous function" is jargon for a function expression that has no name and isn't assigned to anything, e.g.
someFn( function(){...} )
where someFn is called and passed a function that has no name. It may be assigned a name in someFn, or not. Ic could just be referenced as arguments[0].
Passing a function is not delegating, that is jargon for the practice of putting a listener on a parent element and catching bubbling events, it is preferred in cases where it can replace say a click listener on every cell in a table with a single listener on the table.
Anyhow, passing a function is just like passing any other object:
function foo(){
alert('foo');
}
function callIt(fn) {
fn();
}
callIt(foo); // 'foo'
In the above, foo is passed to callIt and assigned to the local variable fn, and is then called.
You pass functions around as variables like so:
var getCellContentByColumnIndex = function(row, index) {
return $(row.children().get(index)).text();
}
function naturalSort(a, b, func) {
//...
var $a = func(a);
var $b = func(b);
//...
}
//usage
naturalSort(x, y, getCellContentByColumnIndex);
This is called using anonymous functions.
Anonymous functions..
var getCellContentByColumnIndex = function(row, index) {
return $(row.children().get(index)).text();
}
will work..rest stuff of calling is already perfect in your code..:)
In JavaScript, functions are treated as first class citizens which mean you can toss them here and there like simple variables. The key is, use the FunctionName when you want to refer to function and use FunctionName() to invoke it.
this line: naturalSort(x, y, getCellContentByColumnIndex);
could have been written as
naturalSort(x, y, function (){
return $(row.children().get(index)).text();
});
In which case it would have been called passing Anonymous Function

Add to a javascript function

I have a function I can't modify:
function addToMe() { doStuff(); }
Can I add to this function? Obviously this syntax is terribly wrong but it's the general idea...
function addToMe() { addToMe() + doOtherStuff(); }
You could store a reference to the original function, and then override it, with a function that calls back the original one, and adds the functionality you desire:
var originalFn = addToMe;
addToMe = function () {
originalFn(); // call the original function
// other stuff
};
You can do this because JavaScript functions are first-class objects.
Edit: If your function receives arguments, you should use apply to pass them to the original function:
addToMe = function () {
originalFn.apply(this, arguments); // preserve the arguments
// other stuff
};
You could also use an auto-executing function expression with an argument to store the reference of the original function, I think it is a little bit cleaner:
addToMe = (function (originalFn) {
return function () {
originalFn.apply(originalFn, arguments); // call the original function
// other stuff
};
})(addToMe); // pass the reference of the original function

Categories

Resources