javascript: "this" in an inner function - javascript

I previously posted a similar question in context to jquery but jquery's inner handling of this confused matters. So to keep it plain and simple consider:
function someCallbackFunction() {
this.name = "Tom";
}
anObject.method(someCallbackFunction);
what is the "this" of someCallbackFunction pointing to when invoked by "anObject"?
That is, when the function "some callback function" is being invoked by an object (within one of its function), what is "this" of (inner) "some callback function" pointing to then? [not the "this" of "outer" (AnObject.function - which is of course pointing to AnObject or another invoking function when called with call or apply)]
I think it should point to the global object (window object) since this inside a closure will point to the global DOM window object (in non-strict mode) or be undefined (in strict mode).

this depends on the way the function is called.
If you call it like mycallback(), this will refer to window object.
If you call it by mycallback.call(object) (or apply), this will refer to object.
http://jsfiddle.net/ydqZ8/1/
function callback()
{
alert(this.toto);
}
window.toto = 0;
var obj = {
toto : "TOTO"
};
callback(); // displays 0
callback.call(obj); // displays TOTO

Try this:
var self = this;
some callback function () {
self.name = "Tom";
}
Object.function (some callback function);

Generally 'this' is pointing to the object hosting the method called. (You may override this by function.call(thisObject, arg1, arg2) or function.apply(thisObject, [argList]).)
A simple example:
var myObject = {
value: 1,
report: function() {
return "value: " + this.value;
}
}
console.log( myObject.report() ); // "value: 1"
Should be quite clear.
Using a constructor and a prototype, it would be:
function Reporter(v) {
this.value = v;
}
Reporter.prototype = {
report: function() { return "value: " + this.value; }
};
var myObject = new Reporter(1);
console.log( myObject.report() ); // "value: 1"
Works just the same. 'this' is the object created by calling "new Reporter(1)" and the 'this' in the prototype is referring to the object which's method "report()" is called.
(The prototype comes only into play, if there's no method "report()" defined in the "myObject" as an own property.)
Now a bit more nested:
function ComplexReporter(v) {
this.value = v;
}
ComplexReporter.prototype = {
report: function() { return "value: " + this.value; },
utils: {
innerReport: function() { return "value: " + this.value; }
}
};
var myObject = new ComplexReporter(1);
console.log( myObject.report() ); // "value: 1"
console.log( myObject.utils.innerReport() ); // "value: undefined"
The first call is just as above and provides the expected result.
In the second call, 'this' is not as might be expected 'myObject', but 'myObject.prototype.utils', which has no property #value of any kind.
This is effectively
ComplexReporter.prototype.utils.innerReport.apply( myObject.prototype.utils, [] );
So as a rule of thumb, 'this' is the entity described by the path to the very last identifier before the last dot when invoking an object's method in dot-notation.
The last example without the prototype (to make it a bit simpler again):
var myComplexObject = {
value: 1,
report: function() {
return "value: " + this.value;
},
utils: {
innerReport: function() {
return "value: " + this.value;
}
}
}
console.log( myComplexObject.report() ); // "value: 1"
console.log( myComplexObject.utils.innerReport() ); // "value: undefined"
// same as myComplexObject.utils.innerReport.apply( myComplexObject.utils, [] );
console.log( myComplexObject.utils.innerReport.apply( myComplexObject, [] ) ); // "value: 1"
And: 'this' is always evaluated the very moment, the function is called (so you can't save the contextual meaning of 'this' when constructing a closure).
I hope, these examples helped a bit in understanding how 'this' works ...
P.S.: If the this-object provided with a call to function.call() or function.apply() is undefined or null, the global object ('self', in a browser identical to 'window') is used as 'this'.

Related

How do I know when to use .bind() on a function in JS?

(I'm aware of this question, but the answers don't quite tell me what I need to know.)
I've come across cases where I need to use .bind() on a function in JavaScript in order to pass this or local/class variables to a function. However, I still don't really know when it's needed.
What is the criteria for knowing when this or local/class variables will or will not be available in a function, exactly? How do you reason about this?
For instance:
When a new anonymous function() { } is created, wrapped, or passed around?
When using a class member function, a class getter/setter function, or an oldschool prototype.function "member function" (of a function acting as a class)?
In the global scope?
In a for or forEach loop, or any of their variants?
In a closure outer or inner function?
In various JS operations like Array.prototype.forEach.call() or [].forEach.call()?
In various JS libraries and scripts, which may have their own custom implementation of things?
The main reason I ask is to be aware of potential pitfalls and to avoid having to rely on trial and error.
The Mozilla Developer Network has some great documentation on this specifying the different cases:
Global context
Function context
using bind
with arrow functions
in object methods
in an object constructor
in DOM event handlers
Check out the links to get an idea of how this works in the different contexts and therefore when bind should be used to force bind a different this context for functions.
Usually, bind is used to transfer 'ownership' of a function. Specifically, in my experience, it was used back before classes were created to force an object method to be tied to the object in question. It is also useful for when using arrow functions because arrow functions have different contexts.
You need to use bind (or a similar approach) when:
The function is a traditional (function keyword) function or method (in a class or object literal), and
That function will be called either in a way that doesn't set this explicitly or that sets it to an incorrect value
The reason is that with a traditional function or method, the value of this is set by the caller, not a part of the function itself. (Details here and here.)
For example, consider:
const obj = {
method() {
console.log(this === obj);
}
};
Now, when we do obj.method(), we're using syntax (calling the result of a property accessor operation) to specify what this will be, so:
obj.method();
// => true
But suppose we do this:
const m = obj.method;
Now, just calling m() will set this to the default this (undefined in strict mode, the global object in loose mode):
m();
// => false
Another way we can explicitly set this for the call is via call (and its cousin apply):
m.call(obj);
// => true
Some functions that call callbacks let you specify what this to use. forEach does, as an argument just after the callback:
[1].forEach(m, obj);
// ^ ^^^---- the value to use as `this` in callback
// \-------- the callback to call
// => true
Here's a live example of those:
const obj = {
method() {
console.log(this === obj);
}
};
obj.method();
// => true, `this` was set to `obj` because you did the call on the
// result of a property accessor
const m = obj.method;
m();
// => false, `this` was the default `this` used when `this` isn't
// specified explicitly via syntax or `call`
m.call(obj);
// => true, `this` was explicitly set via `call`
[1].forEach(m, obj);
// => true, `this` was explicitly set via `forEach`'s `thisArg` argument
So any time you have a function (such as the callback of a forEach, or an event handler), you need bind or a similar mechanism to ensure that the correct this is used.
This isn't true for some other kinds of functions, just traditional (function keyword) functions and methods (such as obj.method above). An arrow function closes over this instead of using the one supplied by the caller, and a bound function (result of using bind) has this bound to it and so ignores any this supplied by the caller.
Credit to T.J. Crowder and Zapparatus for their answers, which provided helpful info. Also helpful were these 4 answers/articles: 1 2 3 4
However, these were either not entirely complete and/or very long-winded. So I have decided to combine all of my findings into one answer, along with code examples.
There are several considerations to factor in when determining whether this or local/class variables will be available in a function:
The function's containing scope
The immediate predecessor in the call chain
Whether the function is called directly or indirectly
Note: there is also strict mode (which yields undefined's rather than window objects) and arrow functions (which do not change this from the containing scope).
Here are the explicit rules:
By default, this is the global object, which in browser world is window.
Inside a function in the global scope, this will still be window, it does not change.
Inside a member function within a class or function-class (new function() { }), inside a function-class's prototype (funcClass.prototype.func = function() { }), inside a function called by a neighboring member function with this, or inside a function mapped in an object ({ key: function() { } }) or stored in an array ([ function() { } ]), if the function is called directly with the class/object/array as the immediate predecessor in the call chain (class.func(), this.func(), obj.func(), or arr[0]()), this refers to the class/object/array instance.
Inside any closure's inner function (any function within a function), inside a returned function, inside a function called with a plain variable reference as its immediate predecessor in the call chain (regardless of where it actually lives!), or inside a function called indirectly (i.e. passed to a function (e.g. .forEach(function() { })) or set to handle an event), this reverts back to window or to whatever the caller may have bound it to (e.g. events may bind it to the triggering object instance).
There is... however... one exception...: if inside a class's member function (and only a class, not a function-class), if a function within that loses its this context (e.g. by being a closure's inner function), it becomes undefined, rather than window...
Here is a JSFiddle with a bunch of code examples:
outputBox = document.getElementById("outputBox");
function print(printMe = "") {
outputBox.innerHTML += printMe;
}
function printLine(printMe = "") {
outputBox.innerHTML += printMe + "<br/>";
}
var someVar = "someVar";
function func(who) {
printLine("Outer func (" + who + "): " + this);
var self = this;
(function() {
printLine("Inner func (" + who + "): " + this);
printLine("Inner func (" + who + ") self: " + self);
})();
}
func("global");
printLine();
func.call(someVar, "someVar");
printLine();
function funcTwo(who) {
printLine("Outer funcTwo (" + who + "): " + this);
var self = this;
return function funcThree() {
printLine("Inner funcThree (" + who + "): " + this);
printLine("Inner funcThree (" + who + ") self: " + self);
};
}
funcTwo("global")();
printLine();
f = funcTwo("global f");
f();
printLine();
funcTwo.call(someVar, "someVar")();
printLine();
object = {
func: function(who) {
printLine("Object outer (" + who + "): " + this);
var self = this;
(function() {
printLine("Object inner (" + who + "): " + this);
printLine("Object inner (" + who + ") self: " + self);
})();
}
}
object.func("good");
printLine();
bad = object.func;
bad("bad");
printLine();
function funcClass(who) {
printLine("funcClass (" + who + "): " + this);
}
funcClass.prototype.func = function() {
printLine("funcClass.prototype.func: " + this);
self = this;
(function() {
printLine("funcClass.func inner: " + this);
printLine("funcClass.func inner self: " + self);
})();
}
fc = funcClass("bad");
printLine();
fc = new funcClass("good");
fc.func("good");
printLine();
class classClass {
constructor() {
printLine("classClass constructor: " + this);
}
func() {
printLine("classClass.func: " + this);
self = this;
(function() {
printLine("classClass.func inner: " + this);
printLine("classClass.func inner self: " + self);
})();
}
funcTwo() {
this.func();
}
}
cc = new classClass();
cc.func();
printLine();
printLine("Calling funcTwo:");
cc.funcTwo();
printLine();
[0].forEach(function(e) {
printLine("[0].forEach: " + this);
printLine("[0].forEach someVar: " + someVar);
});
[0].forEach(function(e) {
printLine("[0].forEach with [0]: " + this);
}, [0]);
printLine();
arr = [
function(who) {
printLine("Array (" + who + "): " + this);
},
1,
10,
100
];
arr[0]("good");
arrFunc = arr[0];
arrFunc("bad");
printLine();
var button = document.getElementById("button");
button.onclick = function() {
printLine("button: " + this);
}
button.click();
button.onclick = func;
button.click();
setTimeout(function() {
printLine();
printLine("setTimeout: " + this);
printLine("setTimeout someVar: " + someVar);
}, 0);
setTimeout(fc.func, 0);
setTimeout(cc.func, 0);
<input id="button" type="button" value="button"/>
<br/><br/>
<div id="outputBox" />
Conclusion: So yeah that's pretty simple.

Javascript Object not reached [duplicate]

I am learning javascript and i came across a doubt. Why is the value of "this" undefined in the first example , but prints out correctly in the second.
example 1:
var myNamespace = {
myObject: {
sayHello: function() {
console.log( "name is " + this.myName );
},
myName: "john"
}
};
var hello = myNamespace.myObject.sayHello;
hello(); // "name is undefined"
example 2:
var myNamespace = {
myObject: {
sayHello: function() {
console.log( "Hi! My name is " + this.myName );
},
myName: "Rebecca"
}
};
var obj = myNamespace.myObject;
obj.sayHello();//"Hi! My name is Rebecca"
Why does the value of "this" changes within the function. What concept am i missing?
First case you are just getting the reference of the function to the vairable hello, and invoking it from global context (window in browsers, global in node), So this becomes what invoked the function except for (bound functions). You can always set the context explicitly using function.call or set the context explicitly to the function using Ecma5 function.bind
hello.call(myNamespace.myObject); //now you are setting the context explicitly during the function call.
or just bind it while getting the function reference.
var hello = myNamespace.myObject.sayHello.bind(myNamespace.myObject); //Now no matter where you call it from `this` will point to the context of myObject
Second case you are invoking it from the object itself so this points to the object.
In the first case, the implicit this object is the global scope. Because there is no myName in the global scope, you get undefined.
If you want a free function with the proper this, use bind:
var hello = myNamespace.myObject.sayHello.bind(myNamespace.myObject);

Passing method name as a callback VS. wrapping it with annoymous function

I have a button #test.
var obj = {
name: "John",
test: function() {
console.log('name '+ this.name );
}
};
$("#test").on( "click", obj.test);
This will log an empty string (logging typeof this.name gives a string).
Edit: I understand this context becomes the button, hence this.name returns nothing.
Versus
var obj = {
name: "John",
test: function() {
console.log('name '+ this.name );
}
};
$("#test").on( "click", function() {
obj.test(); //logs John
});
What is the difference?
Edit: How does wrapping obj.test() with an annon function make it behave differently than above?
It's about resolving this. If you use $("#test").on("click", obj.test); then this will be the button but if you pass a closure then this will be obj.
When I call obj.test then this in test will be obj.
JQuery will set this to be the button when it's clicked so passing obj.test without a reference to obj being this will break your obj.test function.
The best way to resolve this problem is to use Function.prototype.bind (you need the polyfil for IE < 9):
var obj = {
name: "John",
test: function () {
console.log('This is:' + this.toString());
},
toString: function () {
return "It's test"
}
};
$("#test").on("click", function () {
// by the way; this here is the button
console.log("what's this here:", this.toString());
obj.test(); //works but creates a closure
});
$("#test").on("click", obj.test);//breaks, this will be button in test
$("#test").on("click", obj.test.bind(obj));//works
// now to show how this is resolved
window.mytest = obj.test;
window.mytest();// this is window
var obj2 = {
toString: function () {
return "this is obj2";
}
};
obj2.test = obj.test;
obj2.test();//this is obj2
The real difference is what object test is operating on. When you call the function in the second example you are calling the function with the obj context. In the first example you are passing a reference to the function but not the context that it was attached to. In the first case when the function is called it is actually operating the the global scope.
To test this out try putting:
var name = "eiriki"; somewhere in global scope then running your function. It will likely print out eiriki unless you have something else assigning to it already.

What does "this" refer to

what is the this (inside inner functions) referring to in the following code context? Does it point to TimeSpan?
var TimeSpan = function (days, hours, minutes, seconds, milliseconds) {
var attrs = "days hours minutes seconds milliseconds".split(/\s+/);
var gFn = function (attr) {
return function () {
return this[attr];
};
};
var sFn = function (attr) {
return function (val) {
this[attr] = val;
return this;
};
};
}
Thanks
The this value is set implicitly depending on how the function is invoked, there are three cases where this happens:
When a reference with no base-object, or a non-reference is invoked:
myFn(); // the myFn reference has no base object
(function () {})(); // non-reference
The this value will point to the global object 1
When a reference contains a base object, for example:
myObj.method();
The this value inside method will point to myObj.
When the new operator is used:
var obj = new Foo();
The this value inside the Foo function, will point to a newly created object that inherits from Foo.prototype.
The this value can be set also explicitly, by using the call and apply methods, for example, with call:
function test(a) {
return alert(this + a);
}
test.call("hello", " world"); // alerts "hello world"
Or with apply if we need to "apply" a set of arguments from an array to a function:
function test(a, b) {
return alert(this + a + b);
}
var args = ["my ", "world "];
test.apply("hello ", args); // alerts "hello my world"
[1] This has changed on the new ECMAScript 5th Strict Mode, now when a function reference with no base object, or a non-reference is invoked (as the first case), the this value will contain undefined.
This was made because when working with constructor functions, people often forgot to use the new operator when calling the constructor.
When that happened, the this value pointed to the global object, and that ended up adding unwanted global properties.
Now on strict mode, this will contain undefined, and if property lookup is made on it (this.foo = 'foo') we will have a nice TypeError exception, instead of having a global foo property.
this refers to the current object, in this case the function you are inside of. Since everything in JavaScript is an object you can modify the attributes of a function object using the this keyword:
var f = function() {
this.key = "someValue";
}
console.log(f.key); // prints "someValue"
So in this case this should point to the function at the deepest scope level, and not TimeSpan.

javascript prototype and "this" access in closure

I am a beginner in js, and am puzzled by the following code:
Foo = function(arg) {
this.arg = arg;
};
Foo.prototype = {
init: function () {
var f = function () {
alert("current arg: " + this.arg); // am expecting "bar", got undefined
}
f();
}
};
var yo = Foo("bar");
yo.init();
I was expected to get "current arg: bar", but got "current arg: undefined". I noticed that by copying this.arg into a "normal" variable first, and refering this variable in the closure works:
Foo.prototype = {
init: function () {
var yo = this.arg;
var f = function () {
alert("current arg: " + yo); }
f();
}
};
Am I doing something wrong, got wrong expectations, or does it fall into one of the js WTF ?
Vanilla functions will be run with this referring to window. Your second piece of code is a perfect example of how to work around this problem using closures.
(You can also use call and apply to call a function with a particular context.)
It depends on how the function was invoked.
If invoked with keyword new then this refers to the object being constructed (which will be implicitly returned at the end of the function).
If invoked as a normal function, this refers to the global window object.
Example:
// Constructor for Foo,
// (invoke with keyword new!)
function Foo()
{
this.name = "Foo" ;
}
myFoo = new Foo() ;
alert( 'myFoo ' + myFoo.name + '\n' + 'window: ' + window.name ) ; // window.name will be empty
// now if we invoke Foo() WITHOUT keyword NEW
// then all references to `this` inside the
// function Foo will be to the
// __global window object__, i.e. the global window
// object will get clobbered with new properties it shouldn't
// have! (.name!)
Foo() ; // incorrect invokation style!
alert( 'myFoo ' + myFoo.name + '\n' + 'window: ' + window.name ) ;
JavaScript doesn't have "constructors" per se, the only way JavaScript knows that your function is actually a "constructor" is invokation style (namely you using keyword new whenever you invoke it)

Categories

Resources