I've got an array of functions and looking for a concise way to call each one in order.
fns = [
function a() { console.log('a') },
function b() { console.log('b') },
function c() { console.log('c') },
]
this works:
fns.map(function (f) { f() })
and so does this:
fns.map(function (f) { Function.call.call(f) })
however this raises a TypeError:
fns.map(Function.call.call)
Why doesn't the latter example work?
for (var i = 0, len = fns.length; i < len; i++) {
fns[i].call();
};
Here's the working fiddle.
Use Function.prototype.call and Function.prototype.apply as above to call a function. With call and apply you can pass execution scope and arguments to the function call. If you don't need that, you can simply do:
for (var i = 0, len = fns.length; i < len; i++) {
fns[i]();
};
About your code:
Array.prototype.map is a function that takes callback and scope as parameters. In the first two examples, you are using an anonymous function as the callback and you call the parameter passed to it automatically by Array.prototype.map. To expand, your code is the equivalent of this:
function single(element) {
element();
};
fns.map(single);
So the above is entirely correct. Following the same principle by using Function.call.call, you are calling the function f passed as a parameter by map.
But in your third example you are forcing a direct call via Function.call.prototype.call, however in this case, f no longer gets passed as a parameter, which means your Function.call.call will attempt to call undefined, hence you get the TypeError. When you put Function.call.call inside map(), you are NOT passing a callback as an argument.
The call will be immediately. Function.call.call is the exact same thing as Function.call.call(), or Function.call.call(undefined), which will be immediately evaluated when used as you did in your third example.
This simplified code exhibits a similar issue:
var x = Function.call.call;
x(alert);
In this case, once Function.call.call gets called, it won't remember the context from which it originated (i.e. Function.call). To save this context, you could use this unholy construct trick:
Function.call.bind(Function.call)
It returns a new function whereby the context of Function.call is bound to itself, thus saving the context. You can save this expression in a new variable:
var callFn = Function.call.bind(Function.call);
Now, callFn(alert) is identical to alert.call(). Note that any additional arguments will be passed along as is, so callFn(alert, window) will invoke alert.call(window). Understanding this behaviour is important in situations when callFn gets called as part of a callback such as Array.forEach, whereby three arguments are passed in each iteration.
fns.forEach(callFn);
In your case, none of the functions inside fns are using the arguments that are passed, but behind the scenes they're called like this:
fns[0].call(0, fns)
So this equals the element's index (i.e. Number(0)) and arguments[0] equals the array of functions. The keen observer may have noticed that the element's value falls in between the cracks, though it may still be referenced using arguments[0][this] or, alternatively, arguments.callee (deprecated).
The TypeError occurs because Function.prototype.call internally invokes this (with the given context and parameters, but that's not important in the discussion).
Let's rewrite the working
fns.map(function (f) { Function.call.call(f) })
as the equivalent
fns.map(function (f) {
var invoker = Function.call;
invoker.call(f);
})
It is now obvious that invoker is invoked with f as this. When it internally tries to invoke this in turn your function is called, as expected.
Now look at this:
fns.map(Function.call.call);
and the equivalent form
fns.map(function (f) {
var invoker = Function.call;
invoker.call();
})
It should be obvious that here, when invoker is invoked this is undefined; therefore it cannot be invoked itself and this gives rise to the TypeError.
Related
function x() { }
x.a = '1'
let y = x.bind({}) // [Function: bound x]
console.log(y.a)
function y is missing property a defined in x.
Is there any way I can bind a function's this yet still retain its properties like a?
No, there is no way to achieve it in the way you presented. And to understand why you need to know how the .bind() method works.
I think the best explanation can be found on MDN:
The bind() function creates a new bound function, which is an exotic function object (a term from ECMAScript 2015) that wraps the original function object. Calling the bound function generally results in the execution of its wrapped function.
and reading further:
When a bound function is called, it calls internal method [[Call]] on [[BoundTargetFunction]], with following arguments Call(boundThis, ...args). Where boundThis is [[BoundThis]], args is [[BoundArguments]], followed by the arguments passed by the function call.
That means that in your example, the y function is a new function that wraps the x function internally and calls it with changed this context. The polyfill created on the MDN site I linked explains it perfectly.
.bind() returns another function, therefore it does not have any properties you gave it.
I don't know your case but one solution for you might be to treat x as a constructor function like this:
function x() {
this.a = '1';
}
const y = x.bind({});
const newy = new y();
console.log(newy.a);
My solution is like below by copying old function's descriptors to the new one.
const newfunc = func.bind({});
const descriptors = Object.getOwnPropertyDescriptors(func);
Object.defineProperties(newfunc, {
...descriptors
})
From the MDN:
The bind() method creates a new function that, when called, has its this keyword set to the provided value
And I can happily see it working in this example:
(function () {
console.log(this);
}).bind({foo:"bar"})();
which logs Object { foo="bar"}.
But if I chain another bind call, or even a "call" call, I'm still getting the function invoked with "this" assigned to the object passed to the first bind. Examples:
(function () {
console.log(this);
}).bind({foo:"bar"}).bind({oof:"rab"})();
&
(function () {
console.log(this);
}).bind({foo:"bar"}).call({oof:"rab"});
Both log Object { foo="bar"} instead of what I would expect: Object { oof="rab"}.
No matter how many bind calls I chain, only the first one seems to have an effect.
Why?
This might help. I just found out jQuery's version is behaving the same way! :O
jQuery.proxy(
jQuery.proxy(function() {
console.log(this);
},{foo:"bar"})
,{oof:"rab"})();
logs Object { foo="bar"}
It is tempting to think of bind as somehow modifying a function to use a new this. In this (incorrect) interpretation, people think of bind as adding some kind of magic flag to the function telling it to use a different this next time it's called. If that were the case, then it should be possible to "override" and change the magic flag. And one would then ask, what is the reason for arbitrarily restricting the ability to do so?
But in fact, that's not how it works. bind creates and returns a new function which when called invokes the first function with a particular this. The behavior of this newly created function, to use the specified this to call the original function, is burned in when the function is created. It cannot be changed any more than the internals of any other function returned by a function could be changed after the fact.
It may help to look at a real simple implementation of bind:
// NOT the real bind; just an example
Function.prototype.bind = function(ctxt) {
var fn = this;
return function bound_fn() {
return fn.apply(ctxt, arguments);
};
}
my_bound_fn = original_fn.bind(obj);
As you can see, nowhere in bound_fn, the function returned from bind, does it refer to the this with which the bound function was called. It's ignored, so that
my_bound_fn.call(999, arg) // 999 is ignored
or
obj = { fn: function () { console.log(this); } };
obj.fn = obj.fn.bind(other_obj);
obj.fn(); // outputs other_obj; obj is ignored
So I can bind the function returned from bind "again", but that is not rebinding the original function; it's merely binding the outer function, which has no effect on the inner function, since it is already set up to call the underlying function with the context (this value) passed to bind. I can bind again and again but all I end up doing is creating more outer functions which may be bound to something but still end up calling the innermost function returned from the first bind.
Therefore, it is somewhat misleading to say that bind "cannot be overridden".
If I want to "rebind" a function, then I can just do a new binding on the original function. So if I've bound it once:
function orig() { }
my_bound_fn = orig.bind(my_obj);
and then I want to arrange for my original function to be called with some other this, then I don't rebind the bound function:
my_bound_fn = my_bound_fn.bind(my_other_obj); // No effect
Instead, I just create a new function bound to the original one:
my_other_bound_fn = orig.bind(my_other_obj);
I found this line on MDN:
The bind() function creates a new function (a bound function) with the
same function body (internal call property in ECMAScript 5 terms) as
the function it is being called on (the bound function's target
function) with the this value bound to the first argument of bind(),
which cannot be overridden.
so maybe it's really cannot be overridden once it is set.
torazaburo's excellent answer gave me an idea. It would be possible for a bind-like function, instead of baking the receiver (this) into the call inside a closure, to put it as a property on the function object and then use it when the call is made. That would allow a rebind to update the property before the call is made, effectively giving the rebind results that you expected.
For example,
function original_fn() {
document.writeln(JSON.stringify(this));
}
Function.prototype.rebind = function(obj) {
var fn = this;
var bound = function func() {
fn.call(func.receiver, arguments);
};
bound.receiver = obj;
bound.rebind = function(obj) {
this.receiver = obj;
return this;
};
return bound;
}
var bound_fn = original_fn.rebind({foo: 'bar'});
bound_fn();
var rebound_fn = bound_fn.rebind({fred: 'barney'});
rebound_fn();
Or, the output from node.js is as follows.
{ foo: 'bar' }
{ fred: 'barney' }
Note that the first call to rebind is calling the one that was added to Function.prototype since it is being called on ordinary function original_fn, but the second call is calling the rebind that was added as a property to the bound function (and any subsequent calls will call this one, as well). That rebind simply updates receiver and returns the same function object.
It was possible to access the receiver property within the bound function by making it a named function expression.
Okay, this is going to be mostly speculation but I'll try and reason through it.
The ECMAScript specification (which is currently down) states the following for the bind function (emphasis my own):
15.3.4.5 Function.prototype.bind (thisArg [, arg1 [, arg2, …]])
The bind method takes one or more arguments, thisArg and (optionally)
arg1, arg2, etc, and returns a new function object by performing the
following steps:
Let Target be the this value.
If IsCallable(Target) is false, throw a TypeError exception.
Let A be a new (possibly empty) internal list of all of the argument values provided after thisArg (arg1, arg2 etc), in order.
Let F be a new native ECMAScript object .
Set all the internal methods, except for [[Get]], of F as specified in 8.12.
Set the [[Get]] internal property of F as specified in 15.3.5.4.
Set the [[TargetFunction]] internal property of F to Target.
Set the [[BoundThis]] internal property of F to the value of thisArg.
Set the [[BoundArgs]] internal property of F to A.
Set the [[Class]] internal property of F to "Function".
Set the [[Prototype]] internal property of F to the standard built-in Function prototype object as specified in
15.3.3.1.
Set the [[Call]] internal property of F as described in 15.3.4.5.1.
Set the [[Construct]] internal property of F as described in 15.3.4.5.2.
Set the [[HasInstance]] internal property of F as described in 15.3.4.5.3.
If the [[Class]] internal property of Target is "Function", then a. Let L be the length property of Target minus the length of A. b.
Set the length own property of F to either 0 or L, whichever is
larger.
Else set the length own property of F to 0.
Set the attributes of the length own property of F to the values specified in 15.3.5.1.
Set the [[Extensible]] internal property of F to true.
Let thrower be the [[ThrowTypeError]] function Object (13.2.3).
Call the [[DefineOwnProperty]] internal method of F with arguments "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]: thrower,
[[Enumerable]]: false, [[Configurable]]: false}, and false.
Call the [[DefineOwnProperty]] internal method of F with arguments "arguments", PropertyDescriptor {[[Get]]: thrower, [[Set]]: thrower,
[[Enumerable]]: false, [[Configurable]]: false}, and false.
Return F
And when you call a function on your Object that was created with bind:
15.3.4.5.1 [[Call]]
When the [[Call]] internal method of a function object, F, which was created using the bind function is called with a
this value and a list of arguments ExtraArgs, the following steps are
taken:
Let boundArgs be the value of F’s [[BoundArgs]] internal property.
Let boundThis be the value of F’s [[BoundThis]] internal property.
Let target be the value of F’s [[TargetFunction]] internal property.
Let args be a new list containing the same values as the list boundArgs in the same order followed by the same values as the list
ExtraArgs in the same order.
Return the result of calling the [[Call]] internal method of target providing boundThis as the this value and providing args as the
arguments
Call specifies how every function is called. And somewhat resembles the JavaScript call:
someFunction.[[call]](thisValue, arguments) {
}
However when [[call]] is used on a bound function, the thisValue is overridden with the value of [[BoundThis]]. In the case of calling bind a second time, the thisValue that you attempt to override the first with is replaced by [[BoundThis]], essentially incurring no effect whatsoever on the value of thisValue:
boundFunction.[[call]](thisValue, arguments) {
thisValue = boundFunction.[[BoundThis]];
}
You'll notice that if you try and use call or apply then they will also have no effect because their attempt to override the thisValue property will be reversed when [[call]] invokes the next function.
These simplified examples of how bind() works explain it better.
Here is what function bound once looks like:
function bound_function() {
function original_function() {
console.log(self);
}
var self = 1;
original_function();
}
bound_function()
Here is what happens if we wrap original function twice:
function bound_function2() {
function bound_function1() {
function original_function() {
console.log(self);
}
var self = 1;
original_function();
}
var self = 2;
bound_function1();
}
bound_function2()
I think the way to think about it is: When you call bind() the first time the value of 'this' inside the function returned by the call to bind() is FIXED, to the given value. That is possible BECAUSE it wasn't fixed before, it was unbound. But once it is fixed it can not be fixed to anything else because it is no longer unfixed, it is no longer a "variable".
In theory there could be an opposite operation to bind called "unbind" which you could call like:
myFunk.bind(something)
.unbind(); // -> has same behavior as original myFunk
The name "bind" indicates that the (pseudo-) variable 'this' is BOUND to something, it is not simply ASSIGNED a value, which could then be assigned again and again.
When something is "bound" it has a value and that value can not be replaced --because it is "bound". So you would need an unbind() operation to make that possible. But since you assumably have the original function around somewhere
there is no need for "unbind" really.
I agree this behavior is perhaps surprising and unexpected and thus possibly error-prone because if you get a function as argument there seems to be no way to tell whether your bind() on it has any effect or not.
HOWEVER if you don't know much about such function-argument it would also be impossible to know what kind of value you CAN bind to it without breaking the calls it makes to 'this' inside it.
SO the bind() operation itself is rather hazardous. Re-binding would be doubly hazardous. So you are best off trying to avoid doing that if possible.
I'm reading Javascript: The Definitive Guide 6th Edition. It teaches ECMAscript 5. Anyway, it doesn't explain certain things thoroughly, like the call() function for example. This is about the extent of the book's definition:
Any arguments to call() after the first invocation context argument are the values that are passed to the function that is invoked. For example, to pass two numbers to the function f() and invoke it as if it were a method of the object o, you could use code like this:
f.call(o, 1, 2);
In the next section the author builds a map function. I've been studying Ruby so I know how map works. My question is about the implementation using the call() function. It looks like this:
var map = function(a,f, o) {
var results = [];
for(var i = 0, len = a.length; i < len; i++) {
if (i in a)
results[i] = f.call(o || null, a[i], i, a);
}
return results;
};
It then defines a square function and puts map to use:
function square(x){
return x*x;
}
var array = [1,2,3,4,5];
var results = map(array, square);
What is the purpose of the i, and a parameters in the call() function? If I remove them I get the same results.
Array.prototype.map is defined to pass the index and the array to the callback, just in case you need them. For example, instead of square(x), you could use Math.pow(base, exponent):
var results = [1, 2, 3, 4, 5].map(Math.pow);
console.log(results); // [1, 2, 9, 64, 625]
This map behaves in the same way. You don’t have to use the arguments if you don’t need them in a particular case.
Function.call allows you to call a function as though it were a method attached to an object.
What this means is you can have a function that is defined somewhere unrelated to an object, and then you can call that function as though it was a part of that object. This is a long way of saying that when you use Function.call, you are telling the JS engine to use the first parameter whenever you use 'this' inside the function.
So:
function set_field_value(name, value) {
// do stuff
this[name] = value;
}
makes no sense by itself, because the special variable 'this' is not set to anything (meaningful)
But, if you use call, you can set it to whatever you want:
// if my_object = some object:
set_field_value.call(my_object, 'firstname', 'bob');
console.log(my_object.firstname); // prints 'bob'
The only important argument to call is the first one, (in the above case, my_object) because the first argument becomes 'this' inside the function. The rest of the arguments are passed 'as is' to the function.
So - in your example, the i and a arguments are there to make the map function look like other map functions, which provide the array (a) and index (i) that are being worked on.
Hope that helps,
Jay
PS - I strongly recommend the book 'Javascript: the good parts' - it makes a lot more sense than the definitive guide.
f.call in this example equals to square.call, and square requires only one parameter(x), so i and a are totally redundant here (and not used). Only a[i] is used by the function.
However, since you can pass in any function you want as the second parameter of the map function, chances are there will be another function instead of square coming up in the book, and that function would require those additional two parameters as well. Or you can make one example yourself to try it.
function threeParams(a, b, c) {
return [a, b, c]; // simply puts the three parameters in an array and returns it
}
var array = [1,2,3,4,5];
var results = map(array, threeParams);
Your main confusion is not really about the call method. It's more about how javascript treats function arguments.
Forget about call for a moment and let's look at a regular function to minimize the number of things under consideration.
In javascript, functions are allowed to be called with more arguments than is specified. This is not considered an error. The arguments may be accessed via the arguments object:
function foo (arg1) {
alert('second argument is: ' + arguments[1]);
}
foo('hello','world'); // this is not an error
Javascript also allows functions to be called with fewer arguments than specified. Again, this is not considered an error. The unpassed arguments are simply given the value undefined:
function foo (arg1,arg2, arg3) {
alert('third argument is: ' + arg3);
}
foo('hello'); // this is not an error
That's all there is to it. When the function passed to map() is defined to accept one argument but map() calls it with three the remaining two arguments are essentially ignored.
I have some javascript code that takes a function and invokes it again:
var obj = {
// returns the function with prevent default prepended.
run: function(functor, context){
return function(e){
e.preventDefault();
context.call(functor, e);
};
}
}
var myContext = this;
var returnedFunction = obj.run(function(e){alert(e.target)}, myContext);
var returnedFunction(...);
Here is the problem:
On the line where it says context.call(functor, e);
context is always null because the function has gone out of scope.
What do I have to do inside that function so that I can use context?
context has not gone out of scope -- it's probably just null to begin with (and even if it weren't, it likely wouldn't have a call method).
Instead, you probably meant to use functor.call(context, e).
Functions have a call method, which accepts a this/context argument and variable-length following arguments. Objects in JavaScript generally don't have a call method that accepts a function and variable-length following arguments (unless you've added such a method and expect context to have it).
(Question 1)
In Flanagan's JS Definitive Guide, he defines the Function method bind() in case it's not available (wasn't available n ECMAScript 3).
It looks like this:
function bind(f, o) {
if (f.bind) return f.bind(o); // Use the bind method, if there is one
else return function() { // Otherwise, bind it like this
return f.apply(o, arguments);
};
}
He illustrates the use of it with an example (which I have modified to change the 3rd line from f.bind(o)):
function f(y) { return this.x + y; } // This function needs to be bound
var o = { x : 1 }; // An object we'll bind to
var g = bind(f, o); // Calling g(x) invokes o.f(x)
g(2) // => 3
When I first saw this, I thought "Wouldn't arguments refer to the arguments variable within the bind function we're defining? But we want the arguments property of the function we eventually apply it to, like g in the example above..."
I verified that his example did indeed work and surmised that the line return f.apply(o, arguments) isn't evaluated until var g = bind(f, o) up above. That is, I thought, when you return a function, are you just returning the source code for that function, no? Until its evaluated? So I tested this theory by trying out a slightly different version of bind:
function mybind2(f, o) {
var arguments = 6;
return function() { // Otherwise, bind it like this
return f.apply(o, arguments);
};
}
If it's simply returning tbe unevaluated function source, there's no way that it stores arguments = 6 when later evaluated, right? And after checking, I still got g(2) => 3. But then I realized -- if it's just returning unevaluated code, how is the o in return f.apply(o, arguments) getting passed?
So I decided that what must be happening is this:
The o and the arguments variables (even when arguments equals 6) are getting passed on to the function. It's just that when the function g is eventually called, the arguments variable is redefined by the interpreter to be the arguments of g (in g(2)) and hence the original value of arguments that I tried to pass on was replaced. But this implies that it was sort of storing the function as text up until then, because otherwise o and arguments would have simply been data in the program, rather than variables that could be overwritten. Is this explanation correct?
(Question 2) Earlier on the same page, he defines the following function which makes use the apply method to trace a function for debugging:
function trace(o, m) {
var original = o[m]; // Remember original method in the closure.
o[m] = function() { // Now define the new method.
console.log(new Date(), "Entering:", m); // Log message.
var result = original.apply(this, arguments); // Invoke original.
console.log(new Date(), "Exiting:", m); // Log message.
return result; // Return result.
};
}
Wouldn't the this here refer to the function that we're defining, rather than the object o? Or are those two things one and the same?
Question 1
For your first question, let's simplify the example so it's clear what being done:
function bind(func, thisArg) {
return function () {
return func.apply(thisArg, arguments);
};
}
What happens here is that a closure is created that allows the access of the original function and the value of this that is passed. The anonymous function returned will keep the original function in its scope, which ends up being like the following:
var func = function () {};
var thisArg = {};
func.apply(thisArg, [/*arguments*/]);
About your issue with arguments, that variable is implicitly defined in the scope of all functions created, so therefore the inner arguments will shadow the outer arguments, making it work as expected.
Question 2
Your problem is to your misunderstanding of the late binding of this -- this is one of the more confusing things about JavaScript to people used to more object-oriented languages that also have their own this keyword. The value of this is only set when it is called, and where it is called defines the value of this when it is called. It is simply the value of the parent object from where it is at the time the function is called, with the exception of cases where the this value is overridden.
For example, see this:
function a() {return this;};
a(); // global object;
var b = {};
b.a = a;
b.a(); // object b
If this was set when the function was defined, calling b.a would result in the global object, not the b object. Let's also simplify what happens with the second function given:
function trace(obj, property) {
var method = obj[property]; // Remember original method in the closure.
obj[property] = function () { // Now define the new method.
console.log(1); // Log message.
// Invoke original method and return its result
return original.apply(this, arguments);
};
}
What happens in this case is that the original method is stored in a closure. Assigning to the object that the method was originally in does not overwrite the method object. Just like a normal method assignment, the principles of the this value still work the same -- it will return the parent object, not the function that you've defined. If you do a simple assignment:
var obj = {};
obj.foo = function () { return this; };
obj.foo(); // obj
It does what was expected, returns the parent object at the time of calling. Placing your code in a nested function makes no difference in this regard.
Some good resources
If you'd like to learn more about writing code in JavaScript, I'd highly recommend taking a look at Fully Understanding the this Keyword by Cody Lindley -- it goes into much more detail about how the this keyword behaves in different contexts and the things you can do with it.
But this implies that it was sort of storing the function as text up until then, because otherwise o and arguments would have simply been data in the program, rather than variables that could be overwritten. Is this explanation correct?
No. this and arguments are just special variables which are implicitly set when a function is executed. They don't adhere to normal "closure rules". The function definition itself is still evaluated immediately and bind returns a function object.
You can easily verify this with:
var g = bind(f, o);
console.log(typeof g);
Here is a simpler example which doesn't involve higher order functions:
var arguments = 42;
function foo() {
console.log(arguments);
}
foo(1, 2);
I think you see that the definition of foo is evaluated like you'd expect. Yet, console.log(arguments) logs [1, 2] and not 42.
Wouldn't the this here refer to the function that we're defining, rather than the object o? Or are those two things one and the same?
this never refers to the function itself (unless you explicitly set it so). The value of this is completely determined by how the function is called. That's why this is often called "the context". The MDN documentation provides extensive information about this.
Reading material:
MDN - this
MDN - arguments