When I run this code in node, the function foo prints undefined. But if I run the same code in the Chrome or Firefox console I get the right output (oops, global). Can anyone clarify why?
function foo() {
console.log(this.a);
}
function doFoo(fn) {
// `fn` is just another reference to `foo`
fn(); // <-- call-site!
}
var obj = {
a: 2,
foo: foo
};
var a = "oops, global"; // `a` also property on global object
doFoo(obj.foo); // "oops, global"
In browsers, global variables become properties of the window object. And when a function is called without a context, the context also defaults to the window object. Since you're calling foo() without supplying a context, this.a is treated as window.a, which is the global variable.
Node.js has a global object called global, but global variables are not made properties of that object. See Does node.js have equivalent to window object in browser. this.a is treated as global.a, but this is undefined because the variable declaration doesn't assign to it.
Related
So the this of function depends on its execution context (place where its being called)
function foo(){
console.log(this) // will print window
}
foo()
And this one
const obj={
fun:function(){
console.log(this)
}
}
obj.fun(); //will print the obj
I am just confused how the execution context obj gets loaded to call stack from heap
Or am I missing something here ? .Please help me out
Enclosing Scopes
Forgetting about this for a moment. The concept of "execution context" is called Closure in Javascript. The MDN article explains it well.
To get a better sense of it, let's use a variable called a instead of this :
const a = 1;
function foo() {
//can access `a` here
function bar() {
//can also access `a` here
}
}
These functions have access to the outer scopes where they were declared. This is the "execution context", as you called it, though this is the wrong terminology for Javascript. "Scope" or "lexical scope" would be the correct terms.
No matter where you call those functions, they will still be able to print a.
Call Stack and Heap
The MDN article on Closures has this to say:
Closure Scope Chain
Every closure has three scopes:
Local Scope (Own scope)
Outer Functions Scope
Global Scope
The thing to note about these is that they are established at parse time, NOT execution time.
While Javascript does have a call stack and a heap, these have nothing to do with scope or closures. Scopes are a property of the function, and remain the same from the time the Javascript is first parsed until the code has finished running.
About this
this is a variable that depends entirely on how the function was initialized and called. The MDN article may again prove useful here.
Let's look again at your example function, with some inline comments and examples:
function foo() {
console.log(this);
}
// Sure, calling `foo()` right now will print `window` if you're in a browser
foo(); //prints `window` in a browser
// But any use of `new` keyword will change `this` (including classes)
const obj = new Object();
obj.foo = foo;
obj.foo(); //prints `{ foo: [Function: foo] }`
// Similarly, assigning `foo` to a plain object will also change `this`
const obj = {};
obj.foo = foo;
obj.foo(); //prints `{ foo: [Function: foo] }`
// Lastly, any of the `Function.prototype` methods `bind`, `apply`, `call`
// intentionally change `this`
//
// `bind()` returns a new function:
const boundFunction = foo.bind({"this is a property": "and a value, see!?"});
boundFunction(); //prints `{ 'this is a property': 'and a value, see!?' }`
//
//`apply()` and `call()` both execute the function
// (the difference between the two is in how arguments are passed,
// see the MDN articles on
// [apply](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply#syntax) and on
// [call](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call#syntax) for more details)
foo.apply({bar:1}); //prints `{ bar: 1 }`
foo.call({baz: 2}); //prints `{ baz: 2 }`
This in Javascript behaves differently in browser & Node env. this in global context points to window in browser and globalthis in Node. In this case 'This' is pointing on calling object, i.e obj. when a fn is being called js looks this in its lexical environment. You can also bind this context with apply,bind,call.
const obj = {name:'Irfan',fn:function(){console.log(this.name)}} // will print Irfan
Here this is pointing pointing to obj i.e this=obj(this.name=obj.name), but in arrow fn this is not bound by default.
I've experimented with closures and found unexpected behaviour. Can somebody explain why this code works this way, please?
function foo() {
this.a='hello';
return {
aaa:function() {
return a; // this suprises me, how can be here accessed 'a' ?
}
}
}
o=foo();
alert(o.aaa()); // prints 'hello' ! , I expected undefined
I don't understand, why I always use var that=this phrase, If it is possible to access function properties from inner functions directly.
jsfiddle https://jsfiddle.net/5co6f707/
It displays 'hello' because you're not in strict mode, so this is the global window object instead of undefined, and a becomes a global variable when you assign a value to this.a. Since a is a global variable it is accessible everywhere. You could just alert(a); at the very end of your script and it would also display 'hello': https://jsfiddle.net/5co6f707/1/.
It shouldn't work (and doesn't in strict mode) and it shouldn't be used. If you intend to use foo as a constructor then you should use the new keyword when you call it (which would break your code, but in a good way).
When this code is executed:
o=foo();
foo is executed in global context and so this line:
this.a='hello';
adds a property to the global object - window.
When you call your aaa function like this:
o.aaa()
the variable a is not defined within the function so it's looked up on up the scope chain and it's found on window:
function() {
return a; // found on window.a
}
and so window.a is returned.
If variables in the global context are properties (on the global context), then how are they distinguished from other properties on the global context for the purposes of closures?
Obviously this doesn't work:
function foo() {
this.a = 'a';
this.bar = function() { console.log(a); }
}
new foo().bar(); // ReferenceError: a is not defined
Obviously this does work:
var a = 'a';
function bar() {
console.log(a);
}
bar(); // a
But how given that variable a is a "property". Where is my misunderstanding?
They are not distinguished - they are simply both a variable and a property. You might imagine that the global scope is the only scope object that is accessible as an actual language object.
The global environment consists of an environment record that is bound (like a with statement) to the global object, which itself is exposed as the window object in browsers.
To use your constructor example:
function Foo() {
with (this) {
this.a = 'a';
this.bar = function() { console.log(a); }
// Is `a` a variable or a property? It's *one* thing, available as both.
}
}
new Foo().bar(); // 'a'
Global scope is window object in Web browsers. Thus, a variable declaration in the global scope becomes a property of window.
// Somewhere in the global scope...
// Both declarations add a property to window
window.a = "hello world";
var a = "hello world";
In the other hand, when you add a property to an object using this keyword you're not adding it to the built-in window object. This is why the closure won't locate this.a as just a.
If you don't qualify a property name with this it's a variable that can be local or global - understanding global as a property of window object -.
JavaScript runtime looks for a variable in its current scope, otherwise in the parent scope, and so on, until it reaches global scope and, if window doesn't own a property - actually the variable identifier -, your browsers or any JavaScript runtime will determine that the variable is undefined.
In your first example, this.a is scoped to the this context of foo. If you change your code to this:
function foo() {
var a = 'a';
this.bar = function() { console.log(a); }
}
new foo().bar();
It will work. Otherwise you have to do this:
function foo() {
this.a = 'a';
this.bar = function() { console.log(this.a); }
}
new foo().bar();
The reason your first code example doesn't work is that:
console.log(a);
is looking for a variable named a in scope of the bar() method. But you have never declared such a variable. You've only declared a property on some other object with the name a. If you want to refer to that property, you have to use the appropriate object reference like this:
console.log(this.a);
All properties in Javascript MUST include the object that the property is on in order to access them except for properties on the global object which are treated as global variables. So, a is not the same thing as this.a in your context.
Variables are only global if they are declared in the global scope or explicitly attached to the global object (which is window in a browser) or if they are implicitly assigned to without declaration and you are not in strict mode (I call these accidental globals and consider them a horrible thing).
So, here are a couple things you could do.
Use a public instance variable:
function foo() {
this.a = 'a';
this.bar = function() { console.log(this.a); }
}
new foo().bar(); // outputs the member variable a
Or use a constructor local variable (essentially a private instance variable):
function foo() {
var a = 'a';
this.bar = function() { console.log(a); }
}
new foo().bar(); // outputs the member variable a
Or use an actual global (not recommended):
var a;
function foo() {
a = 'a';
this.bar = function() { console.log(a); }
}
new foo().bar(); // outputs the global a
I recently read somewhere (I am really sorry I can't provide the source) that you can use this.varname to access globals in replacement to window.varname to save 2 chars
var myVar = "global";
function myFunc() {
var myVar = "notGlobal";
alert( this.myVar ); //global
}
It seems to work, but I want to know if:
it is safe to use in old browsers
it is cross-browser compatible
it will fail under some weird circumstances
I don't think I'd do it, but it's completely cross-browser compatible if this is referring to the global object (window). Whether it is will depend on how the function in question was called (at global scope, this does indeed refer to the global object), and whether the code in question is in "strict mode" or not. (In strict mode, this does not refer to the global object. Kudos and upvotes to Esailija for pointing that out.)
In non-strict code:
So at global scope:
console.log(this === window); // true if not in strict mode
And similarly, if you have a function you call directly:
function foo() {
console.log(this === window);
}
foo(); // Logs "true"
But, in JavaScript, this is set entirely by how a function is called. So we could call foo setting this to something else:
var obj = {};
foo.call(obj); // Now it logs "false", `this` === `obj` during the call
Similarly:
var obj = {};
obj.f = foo;
obj.f(); // Also logs "false", `this` === `obj` during the call
So in conclusion, at global scope (not in any function call), yes, this is reliably pointing to the global object, and if you control how the function gets called and you call it without setting this to anything else (via call or apply, or by using it from an object property a'la obj.f above), then, again, it will reliably refer to the global object. This is covered by sections 10.4.1 (Entering Global Code) and 10.4.3 (Entering Function Code) of the specification. This is how it's been from the very beginning, I believe; certainly for the last 15 years, so you're unlikely to find a non-compliant environment.
More reading:
Mythical methods
You must remember this
It will not work in modern browsers if someone slaps "use strict" above your code as this will be undefined inside the function.
<script type="text/javascript">
"use strict";
...
function test(){
console.log(this);
}
test(); // undefined
</script>
Note that you can save much more characters just by assigning window to some variable at the top of your code and using that as it will be shortened to some one character variable by a minimizer.
(function(global){
global.foo();
global.bar();
})(window);
would be minimized to (without whitespace):
(function(a){
a.foo();
a.bar();
})(window);
Where as this would not be touched as it's a keyword.
this always has a value. And if this hasnt been overridden somehow, then it will be window where all your globals are.
It will work in all JS implementations, but beware! This is more brittle, and it won't always point to a global variable.
For instance:
var myVar = "global";
var obj = {
myVar: "property",
fn: function() { return this.myVar; }
}
console.log(obj.fn()); // "property"
This fails because in this context this is that object. But when executing a function that is NOT a property of an object, this will default to window, like your example.
Does the value of "this" refer to the global object or the object "o" in the program below?
More importantly, what code could I run to test what the reference of "this" is?
function F() {
function C() {
return this;
}
return C();
}
var o = new F();
It refers to the global object (window).
Edit: Actually, it will refer to both, the global object and o, because they are the same. o will have a reference to the object returned from F() which is the object returned from C() which is the window object ;)
You can call console.log(this) to find out which object it refers to. It should give you a listing of all the methods of the object on the console and you should be able to infer from this which object it is.
For this to work in Firefox, you need Firebug. Don't know for IE.
Update:
#Anurag already showed you how to explicitly set this. If you just want to refer to this of a higher scope, you have to assign it to a variable explicitly. Example:
function F() {
var that = this;
function C() {
console.log(that);
}
C();
}
this refers to the global object in your program. To get this to reference the instance of F, try,
function F() {
function C() {
return this;
}
return C.call(this); // call C with the object of F as a context
}
var o = new F();
On Chrome, you could simply enter the variable to inspect, and its value will get logged. It's similar to doing a console.log(object), but much easier to work with. Here's a screenshot of running this code sample in Chrome. The value of the last statement o is automatically printed, and I printed it once again, just to be sure. It logged DOMWindow which refers to the global window object on the browser.
To add to the other answers:
When a function is called, this is set depending on how it's called. If it's called with myfunc.call or myfunc.apply, then this is set to the first passed argument. If it's called in the 'dotted' form, i.e. myObject.myfunc() then this is set to whatever is before the dot.
There is an exception to this rule, which is that it's possible to bake the value of this in with bind, in which case it will be whatever has been bound. i.e. in var boundfunc = myfunc.bind(myobj); then any time boundfunc is called, it would be like calling myfunc but with this referring to myobj regardless of anything else. Here's an example that does that:
function F() {
var C = function() {
return this;
}.bind(this);
return C();
}
var o = new F();
If none of these cases are applicable, then this is always either the global object (window if you're in a browser), or, if you're in strict mode it's undefined.