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]
Related
I have a JavaScript file which is loaded by require.
// loaded by require()
var a = this; // "this" is an empty object
this.anObject = {name:"An object"};
var aFunction = function() {
var innerThis = this; // "this" is node global object
};
aFunction();
(function(anyParameter){
console.log(anyParameter.anObject);
})(
this // "this" is same having anObject. Not "global"
);
My question is: this in var a = this; is an empty object whereas this statements in functions are shadows of node.js global object. I know this keyword is different in functions but I could not understand why first this is not equal to global and this in functions equals to global.
How does node.js inject global to this in function scopes, and why it does not inject it to the module scope?
Here's a few fundamental facts you must understand to clarify the situation:
In the top-level code in a Node module, this is equivalent to module.exports. That's the empty object you see.
When you use this inside of a function, the value of this is determined anew before each and every execution of the function, and its value is determined by how the function is executed. This means that two invocations of the exact same function object could have different this values if the invocation mechanisms are different (e.g. aFunction() vs. aFunction.call(newThis) vs. emitter.addEventListener("someEvent", aFunction);, etc.) In your case, aFunction() in non-strict mode runs the function with this set to the global object.
When JavaScript files are required as Node modules, the Node engine runs the module code inside of a wrapper function. That module-wrapping function is invoked with a this set to module.exports. (Recall, above, a function may be run with an abitrary this value.)
Thus, you get different this values because each this resides inside a different function: the first is inside of the Node-created module-wrapper function and the second is inside of aFunction.
To understand this, you need to understand that Node.js actually wraps your module code in to a function, like this
(function (exports, require, module, __filename, __dirname) {
var test = function(){
console.log('From test: ' + this);
};
console.log(this);
test();
});
Detailed explanation can be found in this answer.
Now, this wrapped function is actually invoked like this
var args = [self.exports, require, self, filename, dirname];
return compiledWrapper.apply(self.exports, args);
So, this, at the module level, is actually the exports object.
You can confirm that like this
console.log(this, this === module.exports);
// {} true
Summary:
In Javascript the value of this is determined when a function is called. Not when a function is created. In nodeJS in the outermost scope of a module the value of this is the current module.exports object. When a function is called as a property of an object the value of this changes to the object it was called. You can remember this simply by the left-of-the-dot rule:
When a function is called you can determine the value of this by looking at the place of the function invocation. The object left of the dot is the value of this. If there is no object left of the dot the value of this is the module.exports object (window in browsers).
caveats:
This rule does not apply for es2015 arrow function which don't have their own binding of this.
The functions call, apply, and bind can bend the rules regarding the this value.
Example (NodeJS):
console.log(this); // {} , this === module.exports which is an empty object for now
module.exports.foo = 5;
console.log(this); // { foo:5 }
let obj = {
func1: function () { console.log(this); },
func2: () => { console.log(this); }
}
obj.func1(); // obj is left of the dot, so this is obj
obj.func2(); // arrow function don't have their own this
// binding, so this is module.exports, which is{ foo:5 }
Output:
It's because the default global object in a Node.js module is the exports object, and you are calling test() which doesn't specify this. In traditional JS, this points to the global object, with use strict, this will be null.
this can point to anything, it just depends on how you call it.
test(): Uses the global object (exports) as this, unless in strict mode, where this will be null;
test.call({}) or test.apply({}): You are specifying what to use as this (the first parameter)
var obj = {testRef: test}; obj.testRef(): this is set to the left of the ., that is, obj
Countering thefourtheye's answer
It is true that this in the top level of the module is exports, but that doesn't necessarily mean that this inside test() will also point to same thing as where it was called from.
Attempting to prove that this and the global object both point to exports
myGLobal = 5;
this.myGlobal; // 5
I know that using the "this" keyword in an object references the object, but what about nested objects?
var mainObj = {
childObj: {
addProperty: function() {
this.message = "HELLO";
}
}
};
mainObj.childObj.addProperty();
From here, how do I access the "this.message" property, both inside and outside the "childObj" object?
The quick answer: within a different method of mainObj, you can use this.childObj.message, and you can use mainObj.childObj.message from the outside:
var mainObj = {
childObj: {
addProperty: function() {
this.message = "HELLO";
}
},
msg() {
return this.childObj.message;
},
};
mainObj.childObj.addProperty();
console.log(mainObj.msg()); // HELLO
console.log(mainObj.childObj.message); // HELLO
Some context on this:
From the MDN documentation:
A function's this keyword behaves a little differently in JavaScript compared to other languages. It also has some differences between strict mode and non-strict mode.
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 don't provide their own this binding (it retains the this value of the enclosing lexical context).
Basically, depending on the context, this might mean different things:
Global context
Outside of any function, this refers to the global object. In a browser that means this === window.
Function context
In a function, the value of this depends on how you call the function, and whether you are using strict mode or not., basically it could point to the global object or inherit the execution context.
When invoking a function as a method of an object, this will be set to the object that owns the method in the invocation:
//'use strict';
window.message = 'wat';
const obj1 = { message: 'hi 1', hi() { return this.message; }};
const obj2 = { message: 'hi 2', hi() { return this.message; }};
// Normal invocation
console.log(obj1.hi())
console.log(obj2.hi())
// As functions:
const obj1Hi = obj1.hi;
console.log(obj1Hi()); // returns wat on non-strict mode!
// enable use strict to see the error thrown by this defaulting to undefined/
// Swap the functions around
obj1.hi = obj2.hi;
obj2.hi = obj1Hi;
console.log(obj1.hi()) // still uses message from obj1
console.log(obj2.hi()) // still uses message from obj2
// You can even do some `bind` magic to force the context on a function:
console.log(obj1Hi.bind(obj1)()); // hi 1
console.log(obj1Hi.bind(obj2)()); // hi 2
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();
};
}
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.
Consider the following code:
function A() {}
A.prototype.go = function() {
console.log(this); //A { go=function()}
var f = function() {
console.log(this); //Window
};
f();
}
var a = new A();
a.go();
Why does 'this' inside function 'f' refers to the global scope? Why it is not the scope of function 'A' ?
JavaScript has a different concept of what the special name this refers to
than most other programming languages do. There are exactly five different
ways in which the value of this can be bound in the language.
The Global Scope
this;
When using this in global scope, it will simply refer to the global object.
Calling a Function
foo();
Here, this will again refer to the global object.
ES5 Note: In strict mode, the global case no longer exists.
this will instead have the value of undefined in that case.
Calling a Method
test.foo();
In this example, this will refer to test.
Calling a Constructor
new foo();
A function call that is preceded by the new keyword acts as
a constructor. Inside the function, this will refer
to a newly created Object.
Explicit Setting of this
function foo(a, b, c) {}
var bar = {};
foo.apply(bar, [1, 2, 3]); // array will expand to the below
foo.call(bar, 1, 2, 3); // results in a = 1, b = 2, c = 3
When using the call or apply methods of Function.prototype, the value of
this inside the called function gets explicitly set to the first argument
of the corresponding function call.
As a result, in the above example the method case does not apply, and this
inside of foo will be set to bar.
Note: this cannot be used to refer to the object inside of an Object
literal. So var obj = {me: this} will not result in me referring to
obj, since this only gets bound by one of the five listed cases.
Common Pitfalls
While most of these cases make sense, the first one is to be considered another
mis-design of the language because it never has any practical use.
Foo.method = function() {
function test() {
// this is set to the global object
}
test();
}
A common misconception is that this inside of test refers to Foo; while in
fact, it does not.
In order to gain access to Foo from within test, it is necessary to create a
local variable inside of method which refers to Foo.
Foo.method = function() {
var that = this;
function test() {
// Use that instead of this here
}
test();
}
that is just a normal variable name, but it is commonly used for the reference to an
outer this. In combination with closures, it can also
be used to pass this values around.
Assigning Methods
Another thing that does not work in JavaScript is function aliasing, which is
assigning a method to a variable.
var test = someObject.methodTest;
test();
Due to the first case, test now acts like a plain function call; therefore,
this inside it will no longer refer to someObject.
While the late binding of this might seem like a bad idea at first, in
fact, it is what makes prototypal inheritance work.
function Foo() {}
Foo.prototype.method = function() {};
function Bar() {}
Bar.prototype = Foo.prototype;
new Bar().method();
When method gets called on a instance of Bar, this will now refer to that
very instance.
Disclaimer: Shamelessy stolen from my own resources at http://bonsaiden.github.com/JavaScript-Garden/#function.this
The reason why is you are invoking f as a function and not a method. When invoked as a function this is set to window during the execution of the target
// Method invocation. Invoking a member (go) of an object (a). Hence
// inside "go" this === a
a.go();
// Function invocation. Invoking a function directly and not as a member
// of an object. Hence inside "f" this === window
f();
// Function invocation.
var example = a.go;
example();
The scope of all functions is window.
To circumvent that, you can do this:
function A() {}
A.prototype.go = function() {
var self = this;
console.log(self); //A { go=function()}
var f = function() {
console.log(self); //A { go=function()}
};
f();
}
Because function f() is not called without any object reference. Try,
f.apply(this);