I'm trying to figure out the behaviour of "this" keyword in JavaScript. In general I understand how it behaves in different contexts of function calling, but I'm having some trouble when it's part of an arrow function.
I'm using MDN as a source of information: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#Arrow_functions.
I have grasped the general concept that in the case of arrow functions "this" retains the value corresponding to the context of the function definition, but I started having trouble when playing with the last example in that section.
The example goes like this:
var obj = {
bar: function() {
var x = (() => this);
return x;
}
};
var fn = obj.bar();
console.log(fn() === obj); //true
As expected, the console returns true. However, and this is my first doubt, i don't understand why it returns false when I compare obj directly with obj.bar(), since I asume that obj.bar() and fn() invoke the same function:
var obj = {
bar: function() {
var x = (() => this);
return x;
}
};
console.log(obj.bar() === obj); //false
I started playing with the code trying to find further unexpected behaviours, and there are some more doubts. The second one: when I change the type of definition of bar, "this" in the function apparently starts referring to the global object, although I thought the function was being called as a method of obj. ¿Shouldn't the result be the opposite? ¿Is it because now "this" refers to the context of fn?
var obj = {
bar: function() {
var x = function() {
return this;
};
return x;
}
};
var fn = obj.bar();
console.log(fn() === obj); //false
console.log(fn() === this); //true
Third doubt: if I omit fn again and use directly obj.bar() in the comparison, again I find unexpected results. In this case "this" in the function refers neither to the global object nor to obj (I expected the second, since bar() is called as a method of obj).
var obj = {
bar: function() {
var x = function() {
return this;
};
return x;
}
};
console.log(obj.bar() === obj); //false
console.log(obj.bar() === this); //false
When I try to see directly the value returned by obj.bar() I can't get much help from the console:
console.log(obj.bar());
//With arrow definition: () => { length:0
// name:x }
//With the second definition: f() => { length:0
// name:"bar"
// prototype: bar}
I hope someone here can help me understand what's going on, or at least recommend me a more complete resource. Thank you in advance!
I asume that obj.bar() and fn() invoke the same function
No, obj.bar() returns the function x. It's creating a closure.
"this" in the function apparently starts referring to the global object, although I thought the function was being called as a method of obj
No, fn was not called as a method on obj, only bar was. fn() is called as a plain function.
if I omit fn again and use directly obj.bar() in the comparison, "this" in the function refers neither to the global object nor to obj
No. The this in the function is never even evaluated, the function is never called. You only called bar, and then compared the returned function against obj.
When I try to see directly the value returned by obj.bar()
…then the console shows you a function object!
What you are looking at is obj.bar() reutrns a function that is assigned to a variable fn and then fn is invoked fn()
so
var obj = {
bar: function() {
var x = (() => this);
return x;
}
};
console.log(obj.bar()() === obj); //should give you true
Related
Is there a way to get the Function object, while the function is executing?
I am assigning properties to my function, and want to access them. "this" doesn't help. Something like:
a.b=function(){...code...};
a.b.c=100;
I want to access a.b.c from the code in the function, without knowing its own name. "this" refers to a. How can get b?
I tried binding the function to his own object, but I couldn't.
Thank you.
I'm adding this example, I have to repeat after several different "theString" and "someSpecificValues":
Object.defineProperty(theObject, theString, {get: function(...){...}.bind(theObject, someSpecificValues), configurable: true});
You can use a named function expression for this:
var a = {};
a.b = function myFunc() {
console.log(myFunc.c);
};
a.b.c = 100;
a.b();
It allows code inside the function to access the function itself, but does not add the identifier to the enclosing scope.
Edit: Here is a more elaborate example of how the name myFunc only exists within the function:
var a = {};
a.b = function myFunc() {
console.log(myFunc.c);
};
a.b.c = 100;
a.d = function myFunc() {
console.log(myFunc.c);
};
a.d.c = 300;
a.b(); // logs 100
a.d(); // logs 300
console.log(typeof myFunc); // logs "undefined"
// create a myFunc variable
var myFunc = function() {
console.log("nooooooo!!!!");
};
a.b(); // STILL logs 100. the myFunc variable in this scope
// has no effect on the myFunc name that a.b uses
function callFunc(theFunc) {
theFunc();
}
callFunc(a.d); // STILL logs 300
// ===========================
function returnNamedFunction () {
return function myFunc() {
console.log(myFunc.c);
};
}
var iGotAFunction = returnNamedFunction();
iGotAFunction.c = 700;
iGotAFunction(); // logs 700
In the case when you cannot use a named function expression, e.g. when you are using .bind() on it, then an IIFE will suffice most of the time:
var myObj = {};
myObj.theFunc = (function () {
var f = function (arg1, arg2) {
console.log(this.theProp);
console.log(arg1);
console.log(arg2);
console.log(f.lista);
}.bind(myObj, "A!");
return f;
})();
myObj.theProp = "B!";
myObj.theFunc.lista = [1, 2, 3];
myObj.theFunc("C!");
There are two ways to get current function.
One is "almost deprecated" usage of arguments.callee. In function body it always refers to this function.
var a = {};
a.b = function () {
console.log(arguments.callee.c);
};
a.b.c = 100;
a.b();
arguments.callee is forbidden in strict mode. Reference.
The second one is using named function expression as JLRishe pointed.
arguments.callee pros and cons
Advantages:
it can be safely used with bound functions (arguments.callee refers to bound function)
it can be used with functions created using new Function
Disadvantages:
it can slow your program due to disabling certain optimisations
it's considered as almost deprecated
it can't be used in strict mode
Named function expression pros and cons
Advantages:
it's faster than arguments.callee
it's easier to understand how it works
Disadvantages:
it won't work as expected with bound functions (functionName will refer to original function, not bound one)
it can't be used in functions created with new Function
When initially invoking a function, the first this within the first function that is called refers to the parent object foo but on a subsequent function called by that first function this refers to the window object?
var foo = (function Obj(){
var t = this;
return {
getThis: getThis,
getSecondThis: getSecondThis
};
function getThis(){
console.log(this);
console.log(t);
getSecondThis()
return false;
}
function getSecondThis(){
console.log(this);
console.log(t);
return false;
}
})();
foo.getThis();
If I change the call from getSecondThis() to this.getSecondThis() then the this within getSecondThis() refers to the parent object foo see the code below
var foo = (function Obj(){
var t = this;
return {
getThis: getThis,
getSecondThis: getSecondThis
};
function getThis(){
console.log(this);
console.log(t);
this.getSecondThis() //line edited
return false;
}
function getSecondThis(){
console.log(this);
console.log(t);
return false;
}
})();
foo.getThis();
The getSecondThis() is within the scope of the parent object foo but window is returned when this is not specified on the second call.
It's just the way JS binds the calling context: JS binds the context (the this reference) ad-hoc. Meaning: depending on how, where and by what means it is invoked, this will reference a different object.
I've explained this in some detail before here, and in the linked answers found on the bottom
Basically, functions are first class objects, meaning that, like any value, you can assign a function to a multitude of variables/properties. Of course, if you assign a function to an object (as a property), that function is often referred to as a method, and you'd expect this to point to the object that owns that method.
However, as Pointy noted in the comments: An object cannot own another object. Objects can be referenced by one or more properties of another object.
JS will kindly set this to refer to the object that owns the function. But if you then assign the object to a variable, it would make no sense to have this point to that same object. Think of situations where you're passing functions as function arguments (callbacks in jQuery and so on). You probably want this to reference the new context (certainly the case in jQ event handlers!).
If no context is provided, JS sadly defaults the this reference to the global object.
You can explicitly bind a function to a given context using the Function.prototype.bind call.
If you want to specify the context for a single call, you can use Function.prototype.call(context, arg1, arg2); or Function.prototype.apply(context, [args]);
Most larger projects (toolkits like jQ for example) solve this issue by taking advantage of closure scoping. Using the module pattern, for example, is a common, and easy way to control the context. I've explained this, too, complete with graphs to illustrate what is going on :)
Some examples/puzzles to make this easier to follow or more fun:
var obj = (function()
{//function scope
'use strict';//this will be null, instead of global, omit this and this can be window
var obj = {},
funcOne = function()
{
console.log(this, obj, this === obj);
funcTwo(this);
obj.funcTwo();
obj.funcTwo(this);
obj.funcTwo(obj);
},
funcTwo = function(that)
{
console.log(this, obj, this === obj, this === that, that === obj);
};
obj.funcOne = funcOne;
obj.funcTwo = funcTwo;
return obj;//is assigned to the outer var
}());
obj.funcOne();
//output:
//- first logs itself twice, then true
//then calls made in funcOne:
funcTwo()
console.log(this, obj, this === obj, this === that, that === obj);
//- this: undefined (omit 'use strict' and it'll be window), obj,
// this === obj => false, this === that => false, that === obj => true
obj.funcTwo();
console.log(this, obj, this === obj, this === that, that === obj);
//logs obj twice, because obj.funcTwo means the context === obj
//this === obj is true (of course)
//that is undefined, so this === that and that === obj are false
//lastly
obj.funcTwo(this);
obj.funcTwo(obj);
You should be able to work that out. You know the context in which funcOne is being executed, and you know what the effects are of invoking funcTwo as a method of obj
Rule of thumb:
I hesitated to write this, because it's far from accurate, but 8/10 cases. Assuming no code has been meddling with contexts through bind, call, and apply, you can work out the context using this trick:
someObject.someMethod();
/\ ||
|===this===|
//another object:
var obj2 = {
borrowed: someObject.someMethod,
myOwn: function()
{
this.borrowed();
}
};
obj2.myOwn();//this === obj2 (as explained above),
\\
\==> this.borrowed === obj2.borrowed
\\
\==> ~= someObject.someMethod.call(obj2);//function is executed in context of obj2
//simple vars
var alias = someObject.someMethod;//assign to var
alias();//no owner to be seen?
||
?<==|
//non-strict mode:
[window.]alias();
/\ implied ||
|| ||
|==<this>===|
//strict mode
alias.call(undefined);//context is undefined
I know that inside the function it is this.
var func = function {
return this.f === arguments.callee;
// => true, if bound to some object
// => false, if is bound to null, because this.f === undefined
}
var f = func; // not bound to anything;
var obj = {};
obj1.f = func; // bound to obj1 if called as obj1.f(), but not bound if called as func()
var bound = f.bind(obj2) // bound to obj2 if called as obj2.f() or as bound()
Edited:
You can't actually call obj2.f() as f doesn't become a property of obj2
edit end.
The question is: how to find the object, that the function is bound to, outside of this function?
I want to achieve this:
function g(f) {
if (typeof(f) !== 'function') throw 'error: f should be function';
if (f.boundto() === obj)
// this code will run if g(obj1.f) was called
doSomething(f);
// ....
if (f.boundto() === obj2)
// this code will run if g(obj2.f) or g(bound) was called
doSomethingElse(f);
}
and partial application without changing the object that the function is bound to:
function partial(f) {
return f.bind(f.boundto(), arguments.slice(1));
}
Consensus:
You can't do it. Takeaway: use bind and this with great care :)
Partial Application
You can do partial application:
// This lets us call the slice method as a function
// on an array-like object.
var slice = Function.prototype.call.bind(Array.prototype.slice);
function partial(f/*, ...args */) {
if (typeof f != 'function')
throw new TypeError('Function expected');
var args = slice(arguments, 1);
return function(/* ...moreArgs */) {
return f.apply(this, args.concat(slice(arguments)));
};
}
What Object is this Function Bound To?
Additionally, there's a pretty straight-forward solution to the first part of your question. Not sure if this is an option for you, but you can pretty easily monkey-patch things in JS. Monkey-patching bind is totally possible.
var _bind = Function.prototype.apply.bind(Function.prototype.bind);
Object.defineProperty(Function.prototype, 'bind', {
value: function(obj) {
var boundFunction = _bind(this, arguments);
boundFunction.boundObject = obj;
return boundFunction;
}
});
Just run that before any other scripts get run, and any script which uses bind, it will automatically add a boundObject property to the function:
function f() { }
var o = { };
var g = f.bind(o);
g.boundObject === o; // true
(Note: I'm assuming you're in an ES5 environment above due to the fact that you're using bind.)
A function in javascript is not technically bound to anything. It may be declared as a property of an object as in:
var obj = {
fn: function() {}
}
obj.fn();
But, if you get the function into it's own variable by itself, it is not bound to any particular object.
For example, if you did this:
var obj = {
fn: function() {}
}
var func = obj.fn;
func();
Then, when you call func() which in turn executes the fn() function, it will have no association with obj at all when it is called. Associating an object with a function is done by the caller of the function. It is not a property of the function.
If one were to use fn.bind(obj), that creates a new function that just internally executes a call to obj.fn(). It doesn't magically add any new capabilities to javascript. In fact, you can see a polyfill for .bind() to see how it works here on MDN.
If you are expecting this to always be a particular object no matter how a function is called, that is not how javascript works. Unless a function is actually a shim that forces an association with a hard-wird object when it's called (what .bind() does), then a function doesn't have a hard-wired association. The association is done by the caller based on how it calls the function. This is different than some other languages. For example, in C++, you can't call a function without having the right object to associate with it at call time. The language simply won't resolve the function call and you get a compile error.
If you are branching on types in javascript, then you are probably not using the object-oriented capabilities of the language properly or to your best advantage.
Instead of binding the function func to the objects, why not try treating func as an object, that can hold obj1 and obj2 as its properties?
For example:
var func = function {
this.object; // Could be obj1 or obj2
return this.f === arguments.callee;
// => true, if this.object is not null
}
var f = func;
f.object = obj1; // or func.object = obj2;
You can also write a function that handles whether or not the object is obj1 or obj2:
function g(f) {
if (typeof(f) !== 'function') throw 'error: f should be function';
if (f.object === obj)
// this code will run if g(f) was called
doSomething(f);
if (f.object === obj2)
// this code will run if g(f) or g(bound) was called
doSomethingElse(f);
}
The reason is that you want to treat obj1 and obj2 as a property of the function f. However, when you bind, you are adding the function as a property of either obj1 or obj2. It's possible to bind the function to multiple objects, so it doesn't make sense to look for THE one object to which you bound the function; because you're adding the function as a subset of the object.
In this case, since you want to treat the object as a subset of the function, it might make sense to add a property into the function that can hold obj1 or obj2.
If you are the one doing the bind, you can add a field to the function to record the this for later testing.
var that = "hello";
var xyz = function () { console.log(this); }.bind(that);
xyz.that = that;
// xyz is callable as in xyz(), plus you can test xyz.that without calling
When making objects I assumed that this would return the object's instance - instead it seems it's something else. Why is this?
var Obj = {
foo : 'value',
bar : function() { return this.foo; } // Error
bar : function() { return Obj.foo; } // Works
}
Update: I must be missing something because in some cases using this inside objects doesn't work. How come this only references the object instance sometimes?
It does. You have a syntax issue.
Use commas to delimit object members
var Obj = {
foo : 'value',
bar : function() { return this.foo; },
}
within member functions this refers to a reference to the object that the function is called on.
jsFiddle Example
Within a JavaScript function this is set depending on how the function was called.
With your example Obj, assuming you correct the syntax error and use commas between properties:
var Obj = {
foo : 'value', // needs comma, not semicolon
bar : function() { return this.foo; }
}
If you use the "dot" syntax on Obj to call the bar function then this will be automatically set to Obj:
Obj.bar(); // this will be Obj
That's an easy way to ensure this ends up set the way you want. But if you call the function in some other way this could be set to something else:
var nonObjBar = Obj.bar; // get a reference to the function
nonObjBar(); // this will (probably) be `window`, but depends if
// in strict mode or inside some other function, etc
var Obj2 = { foo: "other foo" };
Obj.bar.call(Obj2); // the .call() method sets this to Obj2
// so bar will return Obj2's foo, "other foo"
That last example uses the .call() method on Obj.bar to invoke the function in a way that allows you to set this to anything you like (strict versus non-strict mode affects the way this works for some cases).
It might seem strange if you're coming from languages like Java, but JavaScript functions don't belong to any given object. This behaviour is pretty well defined and on purpose.
After fixing the semi-colon after 'value', this seems to work:
var Obj = {
foo : 'value',
bar : function() { return this.foo; } // Error
};
alert(Obj.bar()); // alerts 'value'
See working jsFiddle here: http://jsfiddle.net/jfriend00/UFPFf/.
When you call a method on an object, the javascript engine sets the this pointer to point to the object for the duration of the method call.
You shouldn't have a semicolon after foo.
Actually when you call Obj.bar you will have this referring to Obj.
See http://jsfiddle.net/AAkbR/
var Obj = {
foo : 'value',
bar : function () { return this.foo; }
};
alert(Obj.bar() === Obj.foo);
Alerts true.
I have been doing some JavaScript development and I encountered this problem. Consider the following code
var obj = {};
obj.bind = function () {
console.info(this);
};
obj.bind();
I'm running the code on the FireBug JavaScript console. The expected result is that this displays a reference to the object in the console.
It actually displays undefined.
But, when I make this change to my code
var obj = {};
obj.bind = function () {
this.x = 'x';
console.info(this);
};
obj.bind();
Now the console displays the expected value of this, which is a reference to the obj object.
Why does this happen?
undefined is the return value of the function, which you'll get because you're not returning a value explicitly.
In both Chrome and Firebug, it correctly displays the Object in the console before the return value undefined.
So if you do:
var obj = {};
obj.bind = function () {
console.info(this);
return "foo";
};
obj.bind();
...you should see something like:
Object { }
"foo"
If Firebug is not displaying the Object when it is empty, you may need to check to make sure you're using the most current version.
In your example, "this" should be obj, just as some commenters pointed out. Here are the details that explain why --
In Javascript, the value of "this" changes depending on how you call the function:
When a function is stored as a property on an object, and you invoke that function by calling obj.foo(), "this" will be obj.
EX:
var obj = {
x: 1,
increment: function() {
this.x = this.x + 1;
};
obj.increment(); // Makes "this" be obj
When you invoke a function using syntax that does not refer to any owning object, "this" will be the global environment.
EX:
function someFunc(a, b) {
return a + b; // If you were to refer to "this" here, it would be the global env.
}
someFunc(5, 6);
When you invoke a function as if it were a constructor by using the new operator, a new object will be instantiated for you, and "this" will point to that new object.
EX:
function SomeConstructor() {
this.x = 42; // under the hood, a new object was instantiated for you, and "this" was set to it.
}
var result = new SomeConstructor(); // note the "new" keyword
// result will be { x:42 }
When you use call() or apply(), you can control what "this" is.
(No example here since it's fairly far from your question. Look up docs for apply() or call() for an example.)