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!)
Related
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.
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);
I understand Hoisting of variables is done in Java Script. I am unable to get why it outputs as undefined
do_something()
{
var foo = 2;
console.log(foo);
} do_something() // it prints 2
do_something()
{
console.log(foo); var foo = 2;
} do_something() // it prints undefined
As javascript do hoisting the second function also should print 2 as per my understand.buy why doesn't it
This is how the interpreter sees your code,
do_something() {
var foo;
console.log(foo); // undefined
foo = 2;
}
do_something();
So it is printing undefined. This is a basic of variable hoisting. Your declarations will be moved to the top, and your assignation will remain in the same place. And the case is different when you use let over var.
Javascript only hoists declarations, not initializations.
var x = y, y = 'A';
console.log(x + y); // undefinedA
Here, x and y are declared before any code is executed, the assignments occur later. At the time "x = y" is evaluated, y exists so no ReferenceError is thrown and its value is 'undefined'. So, x is assigned the undefined value. Then, y is assigned a value of 'A'. Consequently, after the first line, x === undefined && y === 'A', hence the result.
Variable Declaration
Only the declaration is hoisted. the assigned variables are not hoisted. So you are
The second function outputs undefined because you set the variable after you called it. The code you wrote is synchronous which means its read by the interpreter line by line, executed in order. If you don't define the variable before calling it, the console outputs undefined.
This article explains it in more detail.
Only the declaration is hoisted, any assignment to a variable always stays where its originally from.
Can anyone explain the hoisting behavior in the below scenarios?
alert(x);
var x = 10;
Result: alert-undefined
In the below case, x is not defined using 'var' keyword - hence attaches to the global scope.
alert(x);
x = 10; // not given a var
Result: error
In case if we replace x with window.x, we get alert as undefined!
alert(window.x); // calling the variable with window namespace
x = 10; // not given a var
Result: alert-undefined
Can you explain why it is different in calling a variable (which is attached to global scope) with the variable name (x) and with window namespace (window.x)?
The term "hoisting" is often misunderstood to mean that certain statements are moved to the top of their execution context, which is not what happens and why the term should be avoided.
What actually happens is that all declarations are processed first, so any variable declared with var (and any function created by a function declaration) exists before any code is executed. Then code execution begins.
So the variable x is created and assigned the value undefined before any code is executed (per ECMA-262), then later, during execution, it may be assigned some value.
So in the case of:
alert(x);
var x = 10;
x exists when alert is called, but has yet to be assigned a value other than undefined.
In:
alert(x);
x = 10;
x is not declared, so it does not exist when alert is called, hence the error. After the alert (if the code kept running), the assignment to x would create a property of the global (window in a browser) object named x and assign it a value of 10.
In:
alert(window.x);
x = 10;
It is a quirk of ECMAScript that global variables are also available as properties of the global object. The expression window.x attempts to read the x property of the window object. Since no such property exists, the return is undefined. Then the next statement creates x as a global variable and hence window.x now exists with a value of 10.
var x hoists the variable in the entire scope it's valid in, so the name x is available and valid anywhere within the scope. Its initial value is undefined, it only receives its value after the alert.
In case of a plain x, the variable is not hoisted because there's no var, so the bubbling up to window and its creation there only happens on the line x = 10, which is after the alert, which means the variable is entirely undefined and invalid at the time you try to alert it.
Any non-existing property of any object returns undefined, so testing window.x at a time when that property isn't set returns undefined as expected. That's how you do membership testing in Javascript: check whether a specific property equals undefined.
In the first case, var x gets hoisted, so x does exist when you call it (even though the value is undefined)
In the second case, when you say y = 10, you're essentially saying window.y = 10, so there's no hoisting at all, that's why it can't find the variable at all and gives you an error.
alert(x);
var x = 10;
will treated as
var x;
alert(x);
x = 10;
So at the time of alert x has the value undefined by default;
alert(x);
x = 10;
will be treated as
alert(x);
x = 10; // If this line runs in use strict mode then this line will throw error otherwise in non strict mode x will be added to global object ie. window
So will throw error because of x is undefined at the time of alert
alert(window.x);
x = 10;
will be treated as
alert(window.x);
x = 10;
alert will alert undefined because window is an object and it is having no property named as x at the time of alert so undefined
Later the line x = 10; will add the x to global object if in non strict mode, and will throw error if in strict mode
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).