How to access a variable from outer scope in JavaScript - javascript

In the example bellow, I am trying to access the x that is in the function outer.
I was expecting to get 20 as an output, however the output is undefined.
Can someone explain why is that happening, and is there a way to access the outer x?
var x = 10;
function outer() {
var x = 20;
function inner() {
var x = 30;
function printX() {
console.log(outer.x);
}
printX();
}
inner();
}
outer();

Scopes aren't designed like that in JavaScript. In order to attach the variable x to its scope, you would need to reference the object for that scope either by name or by this reference.
What is happening in your example is that your call to printX attempts to log the variable x attached to the function outer. functions derive from objects in JavaScript, and as a result they may have properties attached to them, so instead of giving you a reference error, you get undefined instead, as the variable does not exist.
For more information on scoping, please see my answer on scope in JavaScript.
var x = 10; // Globally scoped variable named "x"
function outer() {
var x = 20; // Locally scoped to outer function variable named "x"
// in outer function, this variable takes precedence over the
// globally scoped x which was 10
function inner() {
var x = 30; // Locally scoped to inner function variable named "x"
// in inner function, this variable takes precedence over the
// parent scoped x which was 20
function printX() {
console.log(outer.x); // Tries to read "x" property of the outer function
// If this had been console.log(x) it would give 30 because it is scoped to the function inner's variable environment
}
printX();
}
inner();
}
outer();
As for what to do going forward, it really depends on what the end goal was. The simple way to fix this would be, as pointed out in comments here, to simply rename the variables. However, that still wouldn't fix the main issue of trying to access the variable by property name instead of by variable name. In order to access a variable by name, simply use its name (and differentiate the names if they share scope), as opposed to trying to access the property name which in this case doesn't exist.

Since it hasn't been mentioned here yet, I will add another possible way to do this by leveraging this and scope by calling the functions with .apply(), like so:
var x = 10;
function outer() {
var x = 20;
function inner() {
var x = 30;
function printX() {
// this now contains all 3 x variables without adding any parameters to any of the functions
console.log("Window x:", this.windowX);
console.log("Outer x:", this.outerX);
console.log("Inner x:", this.innerX);
}
// pass through existing context (which we got from inner.apply(...) down below, as well as add
// inner() x value to the new context we pass to printX()
printX.apply({...this, innerX: x});
}
// pass through existing context (which we got from outer.apply(...) down below, as well as add
// outer() x value to the new context we pass to inner()
inner.apply({...this, outerX: x});
}
// pass through window level x as "this" to outer(). Technically it's still available via window.x,
// but this will be consistent with the others
outer.apply({windowX: x});

You are not creating an object attribute but internal variables. Which you also shadow (that is that you define another with the same name in an inner scope) so that you cannot reach them.
Basically you can access outer.x but you have not set it (only a function scoped variable named x). And to answer your question "if you can get to that variable": No sorry. since you have shadowed it by defining an inner variable with the same name.
What you could do is this:
var x = 10;
function outer() {
outer.x = 20;
function inner() {
inner.x = 30;
function printX() {
console.log(outer.x);
}
printX();
}
inner();
}
outer();
but that would just make the other variables useless, and also setting variables on functions just because you can is not the best practice.
Keep learning.

You can take a look at the concept of scope to get more clarity, but the first x is in the global scope and can be accessed within the function, but you reassigned the variable value to be 20 within the outer function. If you console log the value of x within the outer function, but not inside the inner function, the result will be 20. You assigned the value 30 to x in the inner function, so when you access x within the inner function, it will be 30. If you console.log(x) in each location, you will see the different results.
var x = 10;
function outer() {
var x = 20;
function inner() {
var x = 30;
function printX() {
console.log(x);
}
printX();
}
inner();
console.log(x);
}
outer();
console.log(x);

Related

Bind scoping javascript

I'm trying to figure out scoping of using bind. I'm confused about the output of boundF1. What is going on here?
// console.log(x) ReferenceError: x is not defined
// setting this.x - this here is global or window
this.x = 5;
console.log(x) // 5
function f1() {
console.log(x); // 5
console.log(this.x); // 5
}
// Lexically bound this to window
f2 = () => {
console.log(x); // 5
console.log(this.x); // 5
}
f1()
f2()
boundF1 = f1.bind({x:1});
boundF2 = f2.bind({x:1});
boundF1() // 5 1 Why is this not 1, 1. How is x resolved here? Does it check local vars, then global vars. Would it ever use this? What is the scoping rule?
boundF2() // 5 5
Cause x will always look up the variable in the scope. It has nothing todo with this (the context). When you call .bind, you only set the value of this inside of a function.
When you reference a standalone variable, like console.log(x), the interpreter will try to find a standalone variable with that same name somewhere in the outer scope. Here, the outer scope eventually gets to the global scope, and so console.log(x) resolves to console.log(window.x).
Properties of this do not get added to a function's variable environment; to reference properties of this, you have to do so explicitly, eg:
console.log(this.x);
Not that you should ever use it, but there is with, which lets you reference properties of an object as if they were standalone variables (which sounds like what you were thinking would happen automatically), but it's highly not recommended (and forbidden in strict mode).
this.x = 5;
function f1() {
with (this) {
console.log(x); // 5
console.log(this.x); // 5
}
}
boundF1 = f1.bind({x:1});
boundF1()
in f2, because it's an unbindable arrow function, x and this.x both refer to window.x.
In f1, x will first try to find any locally scoped variable x and if it can't, will search for an x in the global scope. But because it's bound, this no longer refers to window but to the object you bound it to, so this.x is the x-value of the object you bound it to.
In the function f1() in the first print statement you are printing x not this.x, so when you bind it with the object {x:1} with the Function.prototype.bind() you are passing the this as {x:1} object.
But in the function you are reading the value of x from the global scope not from the this context you have bound to.
So when you execute the function f1 and printing x it is looking for the x in local scope first, since it didn't find any it will look up in the parent scope which is the global scope in this case.
If there were no x declared in the global scope you would have gotten a ReferenceError:
function f1() {
console.log(this.x); // 1
console.log(x); // ReferenceError as x is not declared in any scope
}
f1.bind({x:1})();
The scope rules are explained in the MDN docs:
Scopes can also be layered in a hierarchy, so that child scopes have
access to parent scopes, but not vice versa.

Why `this` refers to the parent function's scope

I did read that the this keyword can refer to either the global scope or to the object it is being referred in or to the object to which an event is bound but the below behavior is not understood.
function outer() {
var inner = 4;
function innerFunc() {
var inner = 5;
console.log('inner called with ' + this.inner);
}
return innerFunc;
}
var obj = outer();
obj();
Why does this log 4 instead of 5. AFAIK this should refer to the function it is being referred in and should be available via closure.
TL;DR - this doesn't work like you think it should.
Read more here, here and here.
The this (otherwise known as context) of a function is determined at call time, not at function definition time. This is widely known as this being dynamically bound, and variables from closure being lexically bound.
var obj = outer();
obj();
Calls outer with this = window (or global in the case of Node.js) because you are not in strict mode (otherwise, this = undefined). outer doesn't use this, so this doesn't impact it greatly.
outer then returns a function (innerFunc), and you assign it to obj. You then call obj, again, with this = window (because you didn't specify anything other with .bind() or .call() or .apply()).
You then log this.inner which is equivalent to window.inner which is undefined unless you happen to have a global variable named inner with some value.
The value of this, then, is determined by the caller.
If you had invoked obj like so:
obj.call({inner: 42}); // first argument to fn.call() is the context
You would have seen 42 in your console, regardless of how the function was defined.
A way to mitigate this and retain control over your this is to either use fn.bind() or an arrow function.
Your code snippet is returning undefined, not 4. The reason for this is because the window is calling innerFunc by invoking obj(). Thus, this is referring to the window as that is what called innerFunc (and window.inner is undefined). You can see this by adding a variable inner = 3 at the top of your code (this will make window.inner = 3) and so your function will log 3.
inner = 3; // ie: window.inner = 3
function outer() {
var inner = 4;
function innerFunc() {
var inner = 5;
console.log('inner called with ' + this.inner); // this is window
}
return innerFunc;
}
var obj = outer();
obj();
In this example you are using a function instead of an object. Additionally, you used a variable instead of the this keyword to assign the value. I think the concept that you're thinking of is local scoping
For example,
function parent(){
var n = 5
function child(){
var n = 4 //n is 4 here
}
//n is 5 here
}

Why isn't the following JavaScript function a closure?

var x = 5;
function f0() {
return function () {
var y = 10;
var z = x + y;
console.log('x + y is: ' + z);
}
}
var myFunc = f0();
myFunc();
x = 10;
myFunc();
In the example above, I expected x + y is: 15 to be printed in the second time as well. Because, to the best of my knowledge, what is returned from f0 is a closure. I thought that a closure takes a snapshot of the variables at its environment at the time it is defined. Hence, I thought that changing x with x = 10; wouldn't affect the free variables used in a closure.
But apparently I was wrong. Could you tell me why does changing x change the result of the function which is returned from f0?
Is it because what is returned from f0 is not a closure?
Is it because a closure does not record the values of the variables at the scope it is being returned to?
Is it because of another reason?
Is it because what is returned from f0 is not a closure?
No. Every function is a closure in JavaScript.
Is it because a closure does not record the values of the variables at the scope it is being returned to?
Yes. A closure does not record the values, taking a snapshot of the current state. It simply does record the reference to the scope. And it's the scope it was defined in, not the scope it is returned to.
I am going to digress a little bit in the beginning to get concept of closures clear.
In a language like c++, it is considered a bad idea for a function to return local variables, unless they are allocated on heap.
example:
x foo() {
x obj;
return obj;
}
cout<< foo() << endl;
In above example, obj would be destroyed as soon as we return from foo, and hence, we end up getting garbage as output.
In Javascript, that would be legal, due to the concept of closures.
A closure is a special kind of object that combines two things: a
function, and the environment in which that function was created. The
environment consists of any local variables that were in-scope at the
time that the closure was created.
So, in your case, one variable(y) would be from environment of enclosing function, but x is a global variable, and can be changed from outside function.
The inner enclosing variables(such as y) are also called private variables, since the only way to get to them would be through myFunc() in your case.

Some complex behaviour with 'with' statement and call

var a = ({
x: 10,
foo: function () {
function bar() {
console.log(x);
console.log(y);
console.log(this.x);
}
with (this) {
var x = 20;
var y = 30;
bar.call(this);
}
}
}).foo();
Results in undefined, 30, 20.
Would be greatly appreciated to get some step by step debug-style explanation of how this works.
OK, lets first simplify the code a little. I've refactored out that foo is a method, which is not necessary do demonstrate the unexpected behaviour.
function foo(a) {
// var x, y, bar - hoisting
function bar() {
console.log(x);
console.log(y);
console.log(a.x);
}
with (a) {
var x = 20;
var y = 30;
bar();
}
}
foo({x:10});
So what happens when we call foo?
An execution context is set up and filled with the declared functions and variables. This is what colloquially is referred to as "hoisting". In foo, there are the function bar and the variables x and y (and the function foo itself and its argument a, only in my refactored version). This is the scope to which bar has access to.
The with statement is executed. It exchanges the current lexical environment with one that is based on the a object - any properties of that are accessible like variables now.
The value 20 is assigned to an x. What is this x? When resolving that identifier, the a object is checked and - oh - it has a binding with that name! So we put the value in that binding, which will put the 20 on the .x property of the object.
The value 30 is assigned to an y. What is this y? Again, the current lexical environment is checked but we don't find an y property on the a object. So we go on to the parent environment, which is the one with the x, y, bar variables created above. Indeed, here we find an y variable, so we put the value 30 in that slot.
The bar function is called. Again, a new execution context is set up (like above), with the one from step 1 as its parent scope (which was determined by the lexical position of bar in foo, when the bar function was instantiated - lexical closure). Now we log those three expression of interest:
x resolves to the variable x in the scope of foo, which still has the value undefined.
y resolves to the variable y in the scope of foo, which holds the value 30 that we just assigned.
a.x resolves to the x property of the a object, which holds the value 20 that we just assigned.
It's generally recommended that you avoid with. It's confusing!
That said, it's probably simplest if we just annotate your code. I'll refer to your anonymous object as {} instead of this to avoid ambiguity. And the switch in the ordering here is solely for the sake of reading the code in execution order from top to bottom.
var a = ({
x: 10,
foo: function () {
// entering `with(this)`: all variables are searched against `{}`
// before the engine attempts to create a new variable:
with (this) {
// `var x` creates a local `x`. But, during assignment,
// `x` matches `{}.x`, so `{}.x` is set. **local `x`**
// remains `undefined`.
var x = 20;
// `y` isn't found in `{}`, so it's a local variable
var y = 30;
// execute `bar()`
bar.call(this);
}
// we're now in scope of `{}.foo()`, but not `with(this)`.
function bar() {
// local variable `x` was declared, but never defined.
console.log(x);
// local variable `y` exists in the scope of `{}.foo()`
console.log(y);
// we're still in the "macro" scope of `{}`. So, `this` refers
// to `{}`, which was changed to 20.
console.log(this.x);
}
}
}).foo();
Clear as mud? (Don't use with if you can avoid it!)

How is it that I can add properties to a closure in anonymous self executing function?

why is it that when I do this:
x = 5
(function bla(varX){
varX = 7; console.log(x); //7
})(x);
console.log(x); //5
the x doesn't change, since it's in a closure if I understand correctly,
but here:
x = {a:5}
(function bla(varX){
varX.a = 7; console.log(varX.a);
})(x)
console.log(x.a); //7
Why does x.a gets overwritten and x doesn't?
You can do the same thing with any other function:
var o = {};
function f(x) {
x.val = "foo";
}
f(o);
console.log(o.val);
The object is floating around somewhere on the heap, and both x are merely references to that object. You only need a reference to it to alter an object, no matter how you got that reference.
On the other hand, the statement x = ...; simply overwrites the local variable to refer to something else. This is also why JS does not have "pass by reference" by the traditional (and more useful) definition.
In both functions you have a global x (on line one) and a local x (function bla(x){).
In the first example, you are just changing the values of a series of variables. Changing the value of the local x doesn't touch the global x.
In your second example, you are passing a reference to the object you create to the function, not a copy of it. In the function you modify that object by assigning a new value to one of it's properties. The value of both the global and local x remains the same (a reference to the object).

Categories