This question already has answers here:
The invocation context (this) of the forEach function call
(5 answers)
Closed 5 years ago.
If I run this code, window object gets printed to console.
var arr= [1,2,34,5,6,7,7,8];
arr.forEach(function(e){
console.log(this);
});
Why does it not refer to arr object or specific items in array object? I want to understand the reason behind it, like what's going on. this gets defined using by new, or the object invoking this function, right?
.forEach() specifies the value of this within the iterator based on its 2nd parameter, thisArg.
arr.forEach(callback[, thisArg])
So, it will only use a particular object if you provide it:
arr.forEach(function(e){
console.log(this);
}, arr); // <---
Otherwise, the value of this will be the default value of a normal function call -- either undefined in strict mode or the global object (window in browsers) in non-strict.
function foo(e) {
console.log(this);
}
foo(); // [object Window]
[1].forEach(foo); // (same)
Though, the arr is still provided to the iterator, just as its 3rd argument:
arr.forEach(function (e, i, arr) {
console.log(arr);
});
This comes from two different factors of how the engine will determine the this value for the function, the thisArg optional parameter to forEach, and whether or not the code is in strict mode.
From MDN:
If a thisArg parameter is provided to forEach(), it will be passed to callback when invoked, for use as its this value. Otherwise, the value undefined will be passed for use as its this value. The this value ultimately observable by callback is determined according to the usual rules for determining the this seen by a function.
These rules are elsewhere documented as follows:
Inside a function, the value of this depends on how the function is called.
function f1(){
return this;
}
f1() === window; // global object
In this case, the value of this is not set by the call. Since the code is not in strict mode, the value of this must always be an object so it defaults to the global object. (emphasis added)
Note that this behavior changes in strict mode. If you add "use strict" to the top of the call back, it will log undefined to the console.
In short, if you want the this value to refer to the array, arr, you just need to call forEach like this:
var arr= [1,2,34,5,6,7,7,8];
arr.forEach(function(e){
console.log(this);
}, arr);
Because the specification says so. Relevant parts:
15.4.4.18 Array.prototype.forEach ( callbackfn [ , thisArg ] )
...
5. If thisArg was supplied, let T be thisArg; else let T be undefined.
...
7.c.ii Call the [[Call]] internal method of callbackfn with T as the this value and argument list containing kValue, k, and O.
Now, we all know (hopefully) that if a function is to be called with a this value of null or undefined, this is set to the global object instead. Here is a refresher:
10.4.3 Entering Function Code
If the function code is strict code, set the ThisBinding to thisArg.
Else if thisArg is null or undefined, set the ThisBinding to the global object.
...
As you can also see, this will not point to the global object if we are not in strict mode (the default).
I want to understand the reason behind it
Why it was decided to do it this way can likely only be explained by someone who worked on the language. You could ask at https://esdiscuss.org or http://es-discourse.com/.
this gets defined using by new, or the object invoking this function, right?
Yes. And in this case, it's forEach that invokes your function. Since no special precautions[1] are made, it simply passes nothing for this[2] just like any non-method function call, which will result into the global object as your function is sloppy code[3].
1: Like passing a second argument to forEach, which would be used for this
2: The spec says it should pass undefined
3: Meaning, that it does not use strict mode. In strict code, this would literally really be undefined.
The function you've passed into forEach does not have the same context as your object. The forEach (as well as map etc) functions run detached from any object, so functions passed into them are executed in global scope by default.
You could be explicit and fix that in one of two ways.
give the forEach a context to run in (as suggested by every other answer)
This uses code like:
var arr = [...];
var iterator = function(e) { console.log(this); };
arr.forEach(iterator, this);
make your iteration function explicitly bound to your object first
this uses code like:
var arr = [...];
var iterator = function(e) { console.log(this); };
var iteratorWithThisContext = iterator.bind(this);
arr.forEach(iteratorWithThisContext);
The bind(<context>) function is a core javascript function for taking any function, and any context, and returning a new function that will always execute with the specified context when called, no matter who's doing the calling, or when:
var fn = function() { console.log(this); }
fn(); // window
var fndoc = fn.bind(document);
fndoc(); // document
var arr = [];
var fnarr = fn.bind(arr);
fnarr(); // the array
From MDN:
If a thisArg parameter is provided to forEach(), it will be passed to callback when invoked, for use as its this value. Otherwise, the value undefined will be passed for use as its this value. The this value ultimately observable by callback is determined according to the usual rules for determining the this seen by a function.
If you expect this to be the array, you have to call arr.forEach(function(e){}, arr); using the optional second paramater, thisArg.
Well that's very interesting.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
You can include a second argument for the this object when calling forEach. If not provided, this will be limited to the same scope as if you were simply to write for (var i in arr) {.. }. I suspect they did this so that the forEach function behaves as closely to a built-in syntactical feature of JS as possible.
Because the function being called here is not being called as an object method, this refers to the current object context, and in a browser console window, that's the window object.
This Mozilla Developer Network article goes into great depth about what this means in different contexts.
Related
some updates:
thank all for all your help. i guess this might be the key confusion point: "this" in the param area is not considered "inside" the function, and thus will not follow the rule (this points to the obj the method is called on) mdn specified. example below:
someObj {
someF(//but if "this" shows up here, it doesn't point to someObj) {
//when called, "this" here will point to someObj
}
}
original question:
After reading many docs, I thought I had a good understanding of this, but I was wrong.
The example below is from MDN:
function Counter() {
this.sum = 0;
this.count = 0;
}
Counter.prototype.add = function(array) {
// Here "this" points to obj
array.forEach(function(entry) {
this.sum += entry;
++this.count;
}, this);
// ^---- Note, why it points to obj, not array [2,5,9]???
// Here "this" points to obj
};
const obj = new Counter();
obj.add([2, 5, 9]);
obj.count;
// 3
obj.sum;
// 16
I understand:
The needs to pass in this to a forEach, otherwise in the callback function, this would point to global/window (non-strict mode).
Within most areas of function(array), this points to obj (created from new Counter()), as shown by the comments.
How the callback function uses "this" passed from forEach as the 2nd param. I don't have any question about it
But based on this article on MDN
specifically:
"As an object method, when a function is called as a method of an
object, its this is set to the object the method is called on."
Shouldn't the this (highlighted by ^---Note) passed into the callback point to the array object, i.e., [2,5,9] in this case. Why would it point to obj instead of the array?
Thanks a lot for the help, it's just so confusing.
the needs to pass in "this" in forEach, otherwise "this" in internal function would point to global/window (non-strict mode).
The value of this depends on how the function is called. You can't see the code which calls the callback function you pass to forEach (it is internal to the browser). As it happens, it does call it that way, so it is window (if you don't pass a second argument to forEach).
As an object method, when a function is called as a method of an object, its this is set to the object the method is called on
This is irrelevant. You aren't calling the callback function as a method of an object. You're passing it to forEach … and then forEach is calling it.
You are calling forEach as a method of the array, so inside the forEach function — which you can't see because it is internal to the browser — this will be the array.
Why it would point to obj instead of the array?
Because forEach is explicitly designed to call the callback function in such a way that the second argument to forEach is the this value inside the callback function.
You quoted the documentation that said so.
You will probably see it easier in the following passing in a reference to the object's this as the thisArg of forEach()
Counter.prototype.add = function(array) {
//here "this" points to obj
const _self = this
array.forEach(function(entry) {
// _self passed in as second argument is now `this`
console.log(this === _self) // true
this.sum += entry;
++this.count;
}, _self);
// ^---- passing in reference to the `add` object instance `this`
};
I think your confusion is stemming from the words "called on" in the quote from MDN, and I wouldn't say that is your fault. It doesn't really sound wrong that to say, in the case of obj.add([2, 5, 9]), obj.add is being "called on" [2, 5, 9]. However, what they're trying to say is that the add method is being "called on" obj.
Regardless of how you want to word it, the way it works (in general) is if you're calling a function from a property of an object, that object is what this will point to inside that function. In the case of obj.add([2, 5, 9]), the add function is being accessed via the object obj, so obj is set to this inside that call of add.
This problem is exactly why arrow functions were introduced. They are more tightly bound to your enclosing closure.
function Counter() {
this.sum = 0;
this.count = 0;
}
Counter.prototype.add = function(array) {
array.forEach(entry => {
/*
Because we are using an arrow function here, it is using the
enclosing closure (which originates from `obj`).
*/
this.sum += entry;
++this.count;
});
};
const obj = new Counter();
obj.add([2, 5, 9]);
console.log(obj.sum);
console.log(obj.count);
From the linked documentation:
An arrow function does not have its own this. The this value of the enclosing lexical scope is used; arrow functions follow the normal variable lookup rules. So while searching for this which is not present in current scope, an arrow function ends up finding the this from its enclosing scope.
I'm reading trough this: https://hackernoon.com/functional-javascript-resolving-promises-sequentially-7aac18c4431e
and in one section the author talks about replacing the second arrow function with
Promise.resolve([])
.then(all => funcs[0].then(result => all.concat(result)))
this
Promise.resolve([])
.then(all => funcs[0].then(Array.prototype.concat.bind(all)))
I'm having trouble understanding how this works... Is the returned result being added as an argument implicitly into the concat function?
whatEverMethod.bind(thisValue) binds this to thisValue.
To understand, we can assume (though not actually) every method call
obj.method(arg0)
to equal to
obj.method.apply(obj, arg0)
where the first argument of .apply explicitly tells: which object I am working on (because likely in the definition of .method you may reference to some this value, such as this.prop0 = 10)
What bind does is very simple: bind a this value to a method call, so that when called, no longer uses the default this value based on environment.
For example:
let obj0 = {a: 1}
let obj1 = {a: 2}
obj0.change = function(value) {
this.a = value;
} // when declared, default environment is obj0, since it is a method of obj0
// Now, explicitly bind `this` inside of obj0.change to obj1
let changeFunc = obj0.change.bind(obj1);
// This creates a function that has `this` set to obj1, which has the format changeFunc(value)
changeFunc(10);
console.log(obj1.a) // should be 10, since it is now operating on obj1 (due to binding)
Therefore,
(Array.prototype.concat.bind(all))(someArr)
// is basically
all.concat(someArr)
// due to having the exactly the same `this` value
The reason why we may want to do this is probably all may not be an array. For example, it might be an array-like object, such as the arguments for functions, which looks like an array, but missing common array methods.
bind operates on a function, taking a value for this and returning a new function that calls the original function with the specified this value and the same arguments¹. In other words,
Array.prototype.concat.bind(all)
means
(...args) => Array.prototype.concat.call(all, ...args)
which is
(...args) => all.concat(...args)
and since one argument is being passed, that’s
result => all.concat(result)
There is no reason not to just write the arrow function here, by the way. It’s clearer and has no downsides.
¹ it can also add arguments.
As you must know, the function then receives a function/callback.
The function bind returns a function attaching a specific object as context this.
Array.prototype.concat.bind(all))
^
|
+---- This object 'all' will be the context
'this' for the function 'concat'.
So, the function then will call the callback (passing the all object implicitly) which is the new function returned by the function bind, in this case, the function concat from Array prototype.
Resource
Function.prototype.bind()
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
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.
In JavaScript you can access a function's "functional properties", for instance caller. (Actually, I do not even know if the "functional property" is a right word.)
For example:
func0 = function() {
console.log(typeof func0.caller)
}
func1 = func0
func1()
var o = { func2: func0, func3: function(){ console.log(typeof o.func3.caller) } }
o.func2()
o.func3()
As you can see, you must provide a function name before you can add .caller. But if the function is anonymous or for some reason I do not want to use the name (maybe I plan to rename the fucntion in the future): can I still access caller?
What you are accessing is the arguments object that is 'assigned' to every function. So you don't use the function name. You use the arguments object.
arguments object acts like an array, so arguments[0] returns the first argument passed to the function.
arguments.length
// a property of the arguments object that tells you how many arguments the function has
arguments.caller
// reference to the function that invoked the current function.
arguments.callee() will call the function recursively. Its a reference to the currently executing function.
Is that what you mean?
Use arguments.callee.caller
It seems that the reason this works is because arguments.callee is giving you a reference to the function that is currently executing, and then arguments.caller is referencing the function that invoked that function (which is actually the same function). Maybe that is why using arguments.caller is not advisable.
This is something that I'm sure I should know the answer to, but either I'm just being stupid or I've just somehow never come across this before...
Given the following array, declared in the global scope:
var arr = [function() {
console.dir(this);
}];
I would have expected this to refer to the Window object. However, when calling the function:
arr[0](); //Logs Array
It appears that this actually refers to the array. Then, when I store a reference to the function in another variable and call that, this does refer to the Window object:
var func = arr[0];
func(); //Logs Window
So, why does the context of the function change? Here's a fiddle demonstrating the above two cases.
When you call a function as property of an object, such as obj.func(), this refers to obj.
This is exactly what you are doing here. arr is your object and 0 is the property holding a function.
Note: After all, arrays are just objects and their elements are the values of their properties (though properties are typically numerical strings (all properties are strings)).
See MDN - this for more information, in this case:
When a function is called as a method of an object, its this is set to the object the method is called on.
In your second case, you call the function "standalone", hence this refers to window. If the code was run in strict mode though, this would be undefined.
This is because this keyword is actually an operator which returns the reference to the holder (or owner) of a function where it was called. Since in first case the holder is an array (which is an object) it returns an array. In the second case the holder is the window object.
See this article for more details.
be noted:
in JavaScript, this always refers to the function owner.
you can read more about it at quirksmode.
Because the element is a member of the array, and this always points to the owner object (unless you play with bind()/ call()/ apply() etc). If you're not part of an object, this will be the global object; which is window in the browser environment. The exception to this is if you're in strict mode, where this will be undefined.
What you're doing is effectively the same as:
var ar = {
'0' : function () {
console.dir(this);
}
}
ar[0]();
var func = ar[0];
func();
... which may make more sense conceptually.
http://jsfiddle.net/TVtwr/1/