Lost context in object - javascript

I have this code:
/// global context
function Outer(){
/// Outer context
this.print = function(){
console.log("1: "+this)
inside(); /// "this" is bound to the global object. Why not bound to the Outer object?
function inside(){
console.log("2: "+this)
}
};
}
function print(){
console.log("3: "+this);
}
var obj = new Outer;
obj.print(); /// "this" is bound to the Outer object.
print(); /// "this" is bound to the global object.
Why inside the method call, this has a global object? Can anyone explain this?

That's because, its obeying following 2 JS rules
Rule 1. In Javascript, new function = new scope - that’s the rule.
Rule 2. When a function is called as a method of an object, this is set to the object the method is called on.
Explanation of the code:
For rule 2, this.print() is a feature of Outer object. So this will refer to the Outer object.
So the console.log 1 will print 1: [object Object]
For rule 1, the function inside() in Outer is not a feature of Outer object. So new scope will created and this assigned to window. And that's also same for the last function 'print()'
So console.log 2 will print 2: [object Window]
And console.log 3 will print 3: [object Window] as well
Hope Helps,
Run the code: https://es6console.com/j9bxeati/
Reference: https://toddmotto.com/everything-you-wanted-to-know-about-javascript-scope/#function-scope

As the reference states,
In most cases, the value of this is determined by how a function is called. It can't be set by assignment during execution, and it may be different each time the function is called. ES5 introduced the bind method to set the value of a function's this regardless of how it's called, and ES2015 introduced arrow functions which do provide their own this binding (it remains the this value of the enclosing lexical context).
<...>
Inside a function, the value of this depends on how the function is called.
<...>
Since the following code is not in strict mode, and because the value of this is not set by the call, this will default to the global object
<...>
So, in strict mode, if this was not defined by the execution context, it remains undefined.
The posted code is not executed in strict mode, so this equals to global variable (window) inside the function when it's called like print().
If this is not the desirable behaviour, and print is expected to be called separately from the object it originally was defined on (e.g. when being passed as a callback), the function can be bound to its lexical this with arrow in ES6:
function Outer(){
this.print = () => { ... };
}
In ES5 bind or self = this recipe can be used:
function Outer(){
this.print = (function(){ ... }).bind(this);
}
function Outer(){
var self = this;
this.print = function(){
self;
...
};
}
If the function isn't expected to be called with another context, it can be called with another context in-place:
print.call(this);
Or:
print.bind(this)();

If it is not obvious for you, that probably means you confuse scope and context of the function.
Scope is what vars function has access to during invocation. Context (this) is determined by how function is invoked.
Now look on how you invoke your function 'inside' and answer the question. Does it vividly owned by any object? That is why you have global

The value of this is determined by how a function is called.
Your "inside" function was called inside "print" function, which has an object reference, but that object did not have "inside" function as a property or that object did not invoke "inside" function.
"this" refers to the object which called it, not the scope in which it is called.
Hence, the global object

the value of this is depends on how you are making a call to the intended function and generally leading parent object plays important role in it.
what does it mean by leading parent object?:
sampleObj.getDetails(); // here 'sampleObj' is the leading parent object for 'getDetails()' function
lets try to clear the things using some examples of making call to the function using below sample code snippet.
function globalFunction(){
console.log('global this: ', this);
}
var simpleObj = {
name : 'john'
};
var complexObj = {
outer: function(){
console.log('outer this: ', this);
}
}
globalFunction(); // will log global object as no leading parent object is available
complexObj.outer(); // will log 'complexObj' object as leading parent object is 'complexObj'
complexObj.outer = complexObj.outer.bind(simpleObj);
complexObj.outer(); // will log 'simpleObj' object as we have set preference as 'simpleObj' for this **
complexObj.outer.call(); // will log global object as we arent setting any preference for 'this' ***
complexObj.outer.call(simpleObj); // will log simpleObj object as we have set preference as simpleObj for 'this' ***
complexObj.outer.apply(); // will log global object as we arent setting any preference for 'this' ****
complexObj.outer.apply(simpleObj); // will log simpleObj object as we have set preference as simpleObj for 'this' ****
hopefully it will help you!
** what is bind: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
*** what is call: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call
**** what is apply: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply

You can understand it if you read about arrow functions new in ES6.
Until arrow functions, every new function defined its own this value
(a new object in the case of a constructor, undefined in strict mode
function calls, the base object if the function is called as an
"object method", etc.).
Which means that every function defines it's own this and in case of a simple function it will always be a global object and not the context of a function it is defined in. Just for comparison a function which doesn't define it's own this(an arrow function):
/// global context
function Outer(){
/// Outer context
this.print = function(){
console.log("1: "+this)
let inside = () => {
/// "this" is Outer object here
console.log("2: "+this)
}
inside();
};
}

Related

What is "this" in an arrow function in JavaScript

I have a question about what the "this" points to in an arrow function:
// run this file in nodejs by: node test_this.js
const obj = {
scope:this,
func1:()=>{return this},
func2:function(){
const fn = ()=>this;
return fn();
}
}
console.log("0",obj.scope); // module.exports
console.log("1",obj.func1()); // module.exports
console.log("2",obj.func2()); //obj
const newFunc2 = obj.func2;
console.log("newFunc2",newFunc2()); //global
As I konw, arrow function has no own "this". The scope determines the "this" once the arrow function is declared, and it will never be changed. But why the console.log("2",obj.func2()); outputs the obj? and why console.log("newFunc2",newFunc2()); print a different result global?
The lexical value of this for your obj declaration is module.exports. That's the value of this when your obj variable is declared.
So, that explains why obj.scope is module.exports because that is the value of this at the time your object is declared and initialized with your code scope:this,
As you seem to already know, the arrow function declaration when called with an object as in obj.func1() does not carry through obj as this. Instead, it uses the lexical version of this (what it was at declaration time). As obj.scope shows, that lexical value of this is module.exports. So, the result of console.log("1",obj.func1()) is module.exports as expected (because func1 is defined as an arrow function.
Then, with obj.func2(), that is declared as a normal function so the value of this inside of func2, when called as obj.func2() will be obj. This is how method function calls like obj.func2() work in Javascript (as long as the function is a normal function definition or method definition, not an arrow function).
Lastly, when you do this:
const newFunc2 = obj.func2;
You are getting a reference to JUST the func2 method, all by itself. You've isolated just the method and it has no association at all with the object any more. So, when you call it like this:
console.log("newFunc2",newFunc2());
The object referenced for obj is completely gone and the this value will be set the same as any plain function call will be. The value of this will be set to undefined if you're running strict mode or to the global value if not running strict mode.
For a bit of a review of how all this works or a reference for the future, see:
The 6 ways the value of this is determined inside a function at execution time.
When you're assigning obj.func2 to newFunc2, the function is copied to the new variable, having no connection with the obj anymore. So when you are calling newFunc2(), it's referring to the window object by this in that scope. Below example demonstrate that obj.func2 when defined, doesn't have any information of className. When we copy the function func2 in the class MyObj, this now refers to the new class, hence you get the log this.className = 'MyObj'
const obj = {
scope:this,
func1:()=>{return this},
func2:function(){
const fn = ()=>this;
console.log(this.className)
return fn();
}
}
class MyObj {
constructor() {
this.className = 'MyObj'
this.obj()
}
obj() {
this.func2 = obj.func2
}
print() {
this.func2()
}
}
var myObj = new MyObj()
myObj.print()
.
The JavaScript this keyword refers to the object it belongs to.
It has different values depending on where it is used:
In a method, this refers to the owner object.
Alone, this refers to the global object.
In a function, this refers to the global object.
In a function, in strict mode, this is undefined.
In an event, this refers to the element that received the event.
Methods like call(), and apply() can refer this to any object.
In strict mode, when used alone, this also refers to the Global object [object Window]
"use strict";
var x = this; // x equals [object Window]

Javascript lost context when assigned to other variable

Why in javascript if you reference objects method to some variable it loses that objects context. Can't find any link with explanation what happens under the hood. Except this one which states:
‘this’ refers to the object which ‘owns’ the method which doesn't seam to be true.
var Class = function() {
this.property = 1
}
Class.prototype.method = function() {
return this.property;
}
var obj = new Class();
console.log(obj.method() === 1);
var refToMethod = obj.method; // why refToMethod 'this' is window
console.log(refToMethod() !== 1) // why this is true?
var property = 1;
console.log(refToMethod() === 1)
It depends on how a function is called. If a function is not referenced through being an attribute of an object (e.g. refToMethod) then it will be assigned the "Global context" which is window. However, when a function is an attribute of object (e.g. obj.method), we refer to it as a method, and it is implicitly assigned the context of it's parent object.
JavaScript's context is unlike many languages in that you can override it easily using either .call() or .apply(). Furthermore, ECMAScript 5 introduced a new .bind() method to allow you to create copies of methods which are always bound to the same context. See MDN for more.
var obj = new Class();
obj.method(); // 1;
var unbound = obj.method;
unbound(); // undefined;
// Call and Apply setting the context to obj.
unbound.apply(obj); // 1
unbound.call(obj); // 1;
// ECMAScript 5's bind
var bound = unbound.bind(obj);
bound(); // 1;
From Douglas Crockford's book JavaScript: The Good Parts
The this parameter is very important in object oriented programming,
and its value is determined by the invocation pattern. There are four
patterns of invocation in JavaScript: the method invocation pattern,
the function invocation pattern, the constructor invocation pattern,
and the apply invocation pattern. The patterns differ in how the bonus
parameter this is initialized
The Method Invocation Pattern
When a function is stored as a property of an object, we call it a
method. When a method is invoked, this is bound to that object. If an
invocation expression contains a refinement (that is, a . dot
expression or[subscript] expression), it is invoked as a method
In your example method invocation pattern is
console.log(obj.method() === 1);
and this in this case is bound to the object "Class" and it works as you expected.
The Function Invocation Pattern
When a function is not the property of an object, then it is invoked
as a function:
var sum = add(3, 4); // sum is 7
When a function is invoked with this pattern, this is bound to the
global object. This was a mistake in the design of the language. Had
the language been designed correctly, when the inner function is
invoked, this would still be bound to the this variable of the outer
function. A consequence of this error is that a method cannot employ
an inner function to help it do its work because the inner function
does not share the method’s access to the object as its this is bound
to the wrong value
In your case
var refToMethod = obj.method; // why refToMethod 'this' is window
console.log(refToMethod() !== 1) // why this is true?
refToMethod is bound to the global object "window" in this case
You can find more information about this at JavaScript “this” keyword
Because you only assigned the function method to the refToMethod. When it runs the context is window.
It becomes clear when you print the object and the reference to the function:
console.log(obj); // Prints -> Class {property: 1, method: function}
console.log(refToMethod);
// Prints
// function () {
// return this.property;
// }
In the context of the obj, what is this? It is the Class object, that has an attribute called property.
And what is this in the context of the refToMethod? It is the window object. Thats why your assert failed the first time and it is true the second time. When you declare:
var property = 1;
You are actually declaring an attribute to the window object.

this.variable vs variable child scope

I have some JS code where I want to return my global object's instance in its child method (think fluent interface):
myGlobalVar = function() {
this.self = this;
this.Whatever = function() {
//return self; // why does that FAIL??
return this.self; // why does that WORK??
}
}
I would have assumed that I'd return self but that fails. return this.self succeeds but I figured this in the child function would refer to said function. Why does this work?
In javascript the value of this in a function is determined by the caller of the function.
When you call obj.method(), the value of this is set to obj inside of method(). So, in your example above, when you call myGlobalVar.Whatever(), the value of this inside of Whatever() will be myGlobalVar so to reference sefl, you use this.self as you have discovered.
self all by itself does not work because it is not a variable in the current scope all by itself. It's a property of myGlobalVar and thus can ONLY be referenced as either myGlobalVar.self or this.self when this === myGlobalVar. Javascript does not automatically check properties on the current object in order to resolve a symbol. You can only reach a property of an object by specifically referencing the object that hosts the property as in this.self.
There are other ways that the value of this can be altered. If Whatever() is called without the obj context such as:
var fn = myGlobalVar.Whatever;
fn();
Then, the value of this will be either the global object in regular JS or undefined in JS strict mode.
There are also .call() and .apply() methods on every function that allow you to directly specify what you want the this value to be set to inside the method as in myGlobalVar.Whatever.call(myOtherGlobalVar).
When you try and access a variable, JavaScript, first look for that variable in the current function scope, next, in the enclosed functions scope and then finally in the global scope. In your case, self is a member of this, global object. When you say this.self, and if this refers to the global scope, this.self is same as the self. Unless you change the current context, this will refer to the global object and it should work. Check the following example to understand better.
myGlobalVar = function() {
this.self = "thefourtheye";
this.Whatever = function() {
console.log(self);
console.log(this.self);
console.log(this.self === self);
}
}
var b = {
self: "thethirdeye"
}
myGlobalVar();
Whatever();
console.log("-------------------");
Whatever.apply(b);
Output
thefourtheye
thefourtheye
true
-------------------
thefourtheye
thethirdeye
false
When I am calling Whatever, the second time, I am setting the current context to object b, with apply. So, now this points to b. Now, self is different from this.self. When I refer self, it looks up the hierarchy and finds self in the global scope, it prints thefourtheye, when I say this.self, it looks up self in b and it prints thethirdeye. Hope its clear now.

Difference in function scope and context

Does function in javascript have global and local scope?see the code below
function doSomething()
{
function foo()
{
alert(this);
}
foo();
}
foo();
Here function foo() does not get executed. But the "this" keyword within the function points to global window object,if function doSomething() is executed.That means the function foo() is executing on the global scope .since it is executing in global scope why cant we execute the nested function directly without first executing the doSomething().
You're confusing scope and context. The first is fixed for the given function (unless it's returned by some function, effectively being injected into the outer scope), the second (which, among other things, defines what this refers to inside this function) is given to the function when it's invoked - and in fact can be changed with call and apply.
Consider this:
var someObj = function() {
var internalObj = {
nam: 'internal',
doSomething: function() {
console.log('Called from ' + this.name);
}
}
internalObj.doSomething('Internal'); // 1
var someFunc = internalObj.doSomething;
someFunc(); // 2
return {
nam: 'external',
doSomething: internalObj.doSomething
}
}
var x = new someObj();
x.doSomething(); // 3
setTimeout(x.doSomething, 1000); // 4
setTimeout(x.doSomething.bind({nam:'a new one'}), 2000); // 5
JSFiddle.
In this example we define method 'doSomething' as property of internalObj variable, which is local to this constructor function - if you try to access it outside the someObj, you'll get ReferenceError.
When method is called in the constructor function itself (1), you'll get 'Called from internal' logged - as the method's context object (this) refers to internalObj now.
When we assign this method (its reference) to another local variable and call it from there (2), 'undefined' gets logged instead. Scope obviously didn't change here, but context variable did: it refers to the global object now.
Then we return this method as a result of constructor - and things start getting far more interesting: our function is now available in the outer scope! Note, though, the difference between results of its invokations in (3) and (4): the former gives us external (as the context object here is the one that was created by the constructor), the latter, again, return us undefined even though the syntax is the same. ) It happens because functions sent into timeout and event handlers "lose" their context, in short... unless we fix it with Function.prototype.bind method, like in (5).
JavaScript has function level scope.
Any function declaration will be scoped to its nearest containing function automatically.
Function expressions just return a value (that is a function) so you can scope them in any way you choose.
That means the function foo() is executing on the global scope
You are confusing scope and context.
Scope is determined by where a variable is declared (with var or a special rule like "function declarations are scoped"). It effects where a variable is visible.
Context is determined by how a function is called. It effects what the value of this is.
The two are not connected.

Understanding Javascript scope with "var that = this" [duplicate]

This question already has answers here:
In Javascript, why is the "this" operator inconsistent?
(8 answers)
Closed 9 years ago.
Say I have the following property method in an object:
onReady: function FlashUpload_onReady()
{
Alfresco.util.Ajax.jsonGet({
url: Alfresco.constants.PROXY_URI + "org/app/classification",
successCallback: {
fn: function (o) {
var classButtonMenu = [],
menuLabel, that = this;
var selectButtonClick = function (p_sType, p_aArgs, p_oItem) {
var sText = p_oItem.cfg.getProperty("text");
that.classificationSelectButton.set("label", sText);
};
for (var i in o.json.items) {
classButtonMenu.push({
text: o.json.items[i].classification,
value: o.json.items[i].filename,
onClick: {fn: selectButtonClick}
});
}
this.classificationSelectButton = new YAHOO.widget.Button({
id: this.id + "-appClassification",
type: "menu",
label: classButtonMenu[0].text,
name: "appClassification",
menu: classButtonMenu,
container: this.id + "-appClassificationSection-div"
});
},
scope: this
},
failureMessage: "Failed to retrieve classifications!"
});
It took me some guess work to figure out that in the selectButtonClick function that I needed to reference that instead of this in order to gain access to this.classificationSelectButton (otherwise it comes up undefined), but I'm uncertain as to why I can't use this. My best guess is that any properties in the overall object that gets referenced within new YAHOO.widget.Button somehow looses scope once the constructor function is called.
Could someone please explain why it is that I have to reference classificationSelectButton with var that = this instead of just calling `this.classificationSelectButton'?
The most important thing to understand is that a function object does not have a fixed this value -- the value of this changes depending on how the function is called. We say that a function is invoked with some a particular this value -- the this value is determined at invocation time, not definition time.
If the function is called as a "raw" function (e.g., just do someFunc()), this will be the global object (window in a browser) (or undefined if the function runs in strict mode).
If it is called as a method on an object, this will be the calling object.
If you call a function with call or apply, this is specified as the first argument to call or apply.
If it is called as an event listener (as it is here), this will be the element that is the target of the event.
If it is called as a constructor with new, this will be a newly-created object whose prototype is set to the prototype property of the constructor function.
If the function is the result of a bind operation, the function will always and forever have this set to the first argument of the bind call that produced it. (This is the single exception to the "functions don't have a fixed this" rule -- functions produced by bind actually do have an immutable this.)
Using var that = this; is a way to store the this value at function definition time (rather than function execution time, when this could be anything, depending on how the function was invoked). The solution here is to store the outer value of this in a variable (traditionally called that or self) which is included in the scope of the newly-defined function, because newly-defined functions have access to variables defined in their outer scope.
Because this changes its value based on the context it's run in.
Inside your selectButtonClick function the this will refer to that function's context, not the outer context. So you need to give this a different name in the outer context which it can be referred to by inside the selectButtonClick function.
There's lexical scope: variables declared in functions and arguments passed to functions are visible only inside the function (as well as in its inner functions).
var x = 1; // `1` is now known as `x`
var that = this; // the current meaning of `this` is captured in `that`
The rules of lexical scope are quite intuitive. You assign variables explicitly.
Then there's dynamic scope: this. It's a magical thing that changes it's meaning depending on how you call a function. It's also called context. There are several ways to assign a meaning to it.
Consider a function:
function print() { console.log(this); }
Firstly, the default context is undefined in strict mode and the global object in normal mode:
print(); // Window
Secondly, you can make it a method and call it with a reference to an object followed by a dot followed by a reference to the function:
var obj = {};
obj.printMethod = print;
obj.printMethod(); // Object
Note, that if you call the method without the dot, the context will fall back to the default one:
var printMethod = obj.printMethod;
printMethod(); // Window
Lastly, there is a way to assign a context is by using either call/apply or bind:
print.call(obj, 1, 2); // Object
print.apply(obj, [ 1, 2 ]); // Object
var boundPrint = print.bind(obj);
boundPrint(); // Object
To better understand context, you might want to experiment with such simple examples. John Resig has very nice interactive slides on context in JavaScript, where you can learn and test yourself.
Storing it in a variable lets you access it in other scopes where this may refer to something else.
See https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/this, http://www.quirksmode.org/js/this.html and What is the scope of variables in JavaScript? for more information about the this keyword.
The this reference doesn't work when a method of a class is called from a DOM event. When a method of an object is used as an event handler for onclick, for example, the this pointer points to the DOM node where the event happened. So you have to create a private backup of this in the object.
this is a keyword in javascript, not a default variable defined within every function, hence, as Gareth said, this will refer to the context in which the function is invoked, or the global object if there's no context.

Categories

Resources