Javascript Function Scope vs Object Scope - javascript

I've gotten to the point where I am trying to fill out my javascript knowledge with some of the more advaned concepts.
I think I a pretty much understand how scope works. Where objects inherit from a prototype and then global scope, while functions offer a more traditional block scope within themselves.
What I am have trouble understanding is this:
function a(){
console.log(this.z);
}
a.z = 12;
a(); //returns undefined :(
I was expecting to echo out 12, but of course, it doesn't. Where exactly is z being stored? what does "this" refer to in the example?

When you call a function, JavaScript will set the current context (this) as the object on which it is called. If the function is not attached to any object, by default global object (window object in browsers) will be used *.
So, in this case, this refers to the global object, not a. In the global object, z hasn't been defined yet. That is why it returns undefined.
To get the value 12, you need to access it like this
function a() {
console.log(a.z); // Use `a` itself, instead of `this`.
}
a.z = 12;
a();
* In strict mode, this will be set to undefined, if the function is called without any explicit object reference.

Related

dealing with the this keyword

Just now I started using the this keyword, and I have been wondering, why doesn't "this" work?
var obj = {
thas: this,
obj2: {
obj3: {
thos: this,
lol: "lol",
pap: function() {
console.log(obj.obj2.obj3.lol);
console.log(this.lol);
console.log(thos.lol);
}
}
}
};
obj.obj2.obj3.pap();
take a look at the third console.log().
I am defining the variable so why am I this error: Uncaught ReferenceError: thos is not defined
I think thos needs to referenced in the same way as lol. You used this.lol to access lol, so to access thos you have to access this.thos, which probably wouldn't work as this.thos.lol. OOP gets weird sometimes. If you want to use thos instead, define it outside of the object and give that a try...
EDIT: Here's the modified code:
// this is the new line:
var thos = this;
var obj = {
thas: this,
obj2: {
obj3: {
lol: "lol",
pap: function() {
console.log(obj.obj2.obj3.lol);
console.log(this.lol);
console.log(thos.lol);
}
}
}
};
obj.obj2.obj3.pap();
Hope this helps!
As MDN tells us:
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 ECMAScript 2015 introduced arrow functions whose this is
lexically scoped (it is set to the this value of the enclosing
execution context).
When a function is called as a method of an object, its this is set to the object the method is called on. So, this.lol works fine and equals to just calling lol, since they're both in the scope.
But what happens when thos.lol is called? Since this is set an object property value, the virtual machine thinks this is in global scope:
In the global execution context (outside of any function), this refers to the global object, whether in strict mode or not.
So, you call thos.lol, it sees the global object and gives you undefined error. Try to obj.obj2.obj3.thos.lol; in console and you'll see undefined. Try to obj.obj2.obj3.thos; in console and you'll see the global object.
this refers to the parent object when referred to in an object's property. Inside of the pap() method you can declare:
var thos = this;
Then you can do:
console.log(thos.lol);
you're trying to access thos when it's not in scope. pap: function() { } creates a new function scope that doesn't know anything about thos, so you need to either pass it in or do console.log(obj.obj2.obj3.thos.lol); instead of just console.log(thos.lol);

How does javascript's 'this' binding work when assigning a "method" to another variable?

Consider:
function Thing() {
this.prop = null
}
Thing.prototype.whoIsThis = function() {
console.log(this)
}
a = new Thing()
a.whoIsThis() // logs '> Thing {...}'
f = a.whoIsThis
f() // logs '> Window {...}'
So this is not bound to the Thing in the second call. How does this work in this situation? Isn't a.whoIsThis a "method" of a Thing regardless of any variable its assigned to?
When you say a.whoIsThis, it will refer the function object only. The function object will have no reference to the object on which it is attached. But when you invoke the function, JavaScript dynamically decides the current object and sets that as this inside the function.
This dynamicity allows us to use any object as the current object in the runtime.
But when you simply invoke a function object, without any object reference, by default, JavaScript will set this as the global object (window object in browser) and in Strict mode, this will be set to undefined.
A function's this keyword behaves a little differently in JavaScript compared to other languages. In most cases, the value of 'this' is determined by how a function is called, when 'this' is inside a function.
1) When a function is called as a method of an object, its this is set to the object the method is called on.
2) When a function is called directly, the value of this is not set by the call. Since the code is not in strict mode, the value of this must always be an object so it defaults to the global object. In strict mode, the value of this remains at whatever it's set to when entering the execution context, so 'undefined'.
More info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
This binding is decided at run time and not at author time
having f = a.whoIsThis is just another reference to the function WhoIsThis
now Imagine If you have the function declared in the global scope..which is really what f is now..and when calling f JS at run time run the function as if it was declared in the global scope(as I mentioned) so It asks ..what this refers to if I'm in the global scope => default binding rule would answer..it's simply the global(window) object

'this' in a function VS 'this' in a method

From Javascript-Garden:
Foo.method = function() {
function test() {
//this is set to the global object
}
test();
}
In order to gain access to Foo from within test, it is necessary to create a local variable inside of method that refers to Foo:
Foo.method = function() {
var that = this;
function test(){
//Use that instead of this here
}
test();
}
Could anyone explain this? As far as I understood, this refers to the global object if it's called in the global scope. But here it's called inside of a function, which is inside a method (first example). Why exactly does it refer to the global object, while the second example doesn't?
As far as I understood, this refers to the global object if it's called in the global scope.
No. this will refer to the default object if the function is called without explicit context. The scope is irrelevant. (In strict mode it will refer to undefined instead).
Why exactly does it refer to the global object
We can't tell what it refers to. The value of this is determined by how the function is called, not how it is defined.
Now you have updated the example, we can see that it is called without context, so this (in the inner function) will be the default object, which in a web browser is window (it would be undefined in strict mode).
while the second example doesn't?
In the second example, the inner function doesn't use this (which will have the same value as the previous example).
The second example uses that instead. that is defined in the scope of the outer function and is set to whatever the value of this is when that function is called.
Assuming that function is called as Foo.method() then (outer) this (and hence that) will be Foo because that is the context on which method was called.
this in a function isn't set when you define the function. It's only dynamically defined to the receiver of the function call.
If you call foo.test(), this in test will be foo.
But if you do
var f = foo.test;
f();
then this in f (which is foo.test) will be the external object (window if you're executing it at the root level).
It would be the same with
foo.test.call(window);
The second example uses a closure to place the outer functions variables on the scope chain of the inner function.
Foo.method = function() {
var that = this;
function test(){
//This function has access to the outer variables scope chain.
}
}

Function context ("this") in nested functions

When you invoke a top-level function in Javascript, the this keyword inside the function refers to the default object (window if in a browser). My understanding is that it's a special case of invoking the function as method, because by default it is invoked on window (as explained in John Resig's book, Secrets of the JavaScript Ninja, page 49). And indeed both invocations in the following code are identical.
function func() {
return this;
}
// invoke as a top-level function
console.log(func() === window); // true
// invoke as a method of window
console.log(window.func() === window); // true
So far so good... Now here is the part I don't understand:
When a function is nested in another function and invoked without specifying an object to invoke on, the this keyword inside the function also refers to window. But the inner function cannot be invoked on window (see code below).
function outerFunc() {
function innerFunc() {
return this;
}
// invoke without window.* - OK
console.log(innerFunc() === window); // true
// invoke on window
//window.innerFunc(); - error (window has no such method)
console.log(window.innerFunc) // undefined
}
outerFunc();
It makes perfect sense that the nested function isn't available on window, as it is after all nested... But then I don't understand why the this keyword refers to window, as if the function was invoked on window. What am I missing here?
EDIT
Here is a summary of the great answers below and some of my follow up research.
It is incorrect to say that invoking a function "normally" is the same as invoking it as a method of window. This is only correct if the function is defined globally.
The function context (the value of the this keyword) does not depend on where / how the function is defined, but on how it is being invoked.
Assuming that the code is not running in in strict mode, Invoking a function "normally" will have the function context set to to window (when running in a browser, or the corresponding global object in other environments).
An exception to the above rules is the use of bind to create a function. In this case even if the function is invoked "normally", it could have a context other than window. That is, in this case the context is determined by how you create the function, rather than how you invoke it. Although strictly speaking this isn't accurate, because bind creates a new function that internally invokes the given function using apply. The context of that new function will still be determined by the way it's invoked, but it shields the context of the function it internally invokes by using apply.
By invoking "normally" I refer to the following simple way of invocation:
myFunction();
To complete the picture, here is a brief coverage of other ways of invocation and the corresponding context:
As a property of an object (method) - the context is the object
Using apply or call - the context is specified explicitly
With the new operator (as a constructor) - the context is a newly created object
Feel free to update the above as necessary, for the benefit of people with similar questions. Thanks!
You can call any function that is in scope with functionName(). Since you haven't called it on an object, it will be called in the context of the default object (window). (IIRC, it will be called in the context of undefined if you are in strict mode).
The default object for context has nothing to do with where a function is defined or what scope that function appears in. It is simply the default object.
If a function is a property of an object, you can call it as reference.to.object.function(), and it will be called in the context of object instead of the default object.
Other things that change the context are the new keyword and the apply, call, and bind methods.
In JavaScript, when a function is invoked without an explicit context, the context is the global object. In the case of web browsers, the global object is window.
Additionally, JavaScript has functional scope, so any variables or functions within a function are not accessible in a scope outside of that function. This is why you can't access window.innerFunc.
Whether a function is nested inside another one has nothing to do with the value of this when the function is called. The only things that matter are:
If the function is "found" by traversing a property on an object, then the value of this will be a reference to that object:
someObject.prop( whatever );
It doesn't matter how the function was declared.
If you use call() or apply() to invoke a function, then the value of this is taken from the first argument to whichever of those functions you use.
If you've created a bound wrapper for the function with bind(), then the value of this will be as requested when bind() was called.
If you're calling a function as a constructor with new, then this will refer to the newly-created object instance.
Otherwise, this is either a reference to the global context, or else it's undefined (in "strict" mode or in an ES5-compliant runtime).
The "location" in the code where a function is defined does matter, of course, in that the scope includes whatever symbols it includes, and those are available to the function regardless of how a reference to it is obtained.
It does not depend where the function is declared but how it is called:
var obj = {
f: function() {
return this;
}
}
var f = obj.f;
console.log(obj.f()) // obj
console.log(f()) // window/default obj
Or in other words. The syntax obj.f() executes the function with this=obj while f() executes the function with this=window. In JavaScript the caller specifies the value of this.
When you define func in the global scope, it actually is assigned as a property of the window object. That is, the window object holds all globally scoped variables. (*) Therefore, func and window.func represent the same thing. innerFunc is defined inside a function scope and is not available outside of that scope. Therefore, window.innerFunc is (still) undefined.
However, the this context is determined by how you call the function. When you call a method like obj.method(), the this context is set to obj. On the other hand, you can also call the method on its own:
var f = obj.func;
f(); // in this call: this === window
In this case, you're not calling a function on an object and thus the this context is set to the default. The default however is the global scope and as stated above, this is represented by window.
You can always override the this context by using Function.prototype.call() or Function.prototype.apply() which take a this context as first argument. For example:
var f = obj.func;
f.call(obj); // in this call: this == obj
(*) Note that this only applies to JavaScript running inside a browser. In other environments, this may differ. For example, in Node.js the GLOBAL variable holds the global scope.

The global object is an object of which class?

I want to know what the global object in JavaScript is and to which class this object belongs to.
And how are Infinity, NaN and undefined part of the global object?
Variable scope is defined in JavaScript by a function, and functions can be nested inside other functions.
function foo() {
// new variable scope in here
var a = "a";
function bar() {
// another nested variable scope
var b = "b";
}
bar();
}
foo();
EXCEPT there is a default "global" variable scope that is defined when your program runs. It is the base variable scope in which all function created scopes are nested.
So what?
Well, every variable scope has a variable object (or more accurately, a "binding" object). It's an internal object to which all the local variables you create are bound.
This variable object is not directly accessible. You can only add properties to it by declaring a local variable (or function parameter, or function declaration). And you can only access properties via the variable names.
Again, so what?
Well the "global" variable scope is unique. It exposes this internal variable object by automatically defining a property on the object that refers back to the object itself. In a browser, the property is named window.
Because a property is placed on the object that refers back to the object, and because properties on the object become variables, we now have a direct access to the global variable object.
You can test this by observing that the window.window property is an equal reference to the window variable.
alert(window.window === window); // true
As a result, we can add a property to the object window.foo = "bar";, and it show up as a global variable alert(foo); // "bar".
Note that the only variable scope that exposes this internal object is the global scope. None of the function scopes expose it.
Also note that the ECMAScript specification does not require that the global variable object be exposed. It is up to the implementation to decide.
There are no real classes, but if you mean the prototype chain of the global object, the specification doesn't say much:
The values of the [[Prototype]] and [[Class]] internal properties of the global object are implementation-dependent.
([[Class]] is used in e.g. window.toString() so that you may get "[object global]".)
The three values you mention are properties of the global object, e.g.:
Infinity === window.Infinity; // true (in a browser the global object is window)
You cannot overwrite these variables, so you can see them as literals. But in reality they are properties of the global object, and thus you can refer to them as variables ("global variables").

Categories