Different handling of 'this' in Node.js and Browser - javascript

I have Node.js v8.10.0 locally installed. I wrote a simple script to play with 'this':
var x = 1;
var fn = function (a) {
var x = a;
console.log(`local x = ${x}`);
console.log(`global x = ${this.x}`);
}
fn(10);
When I execute script via Node.js I get following result:
local x = 10
global x = undefined
When I execute script in Chrome I get the following result:
local x = 10
global x = 1
Could you please explain to me, why Node.js doesn't see x in global scope?

Could you please explain to me, why Node.js doesn't see x in global scope?
It does, if you run it in Node console. If you run it in as a file, x is in the file's scope, not global scope.
By the way, in Node, you can use global to explicitly see the global scope, just like you'd use window in a browser. Thus,
console.log(global == this)
will give you two different answers depending on whether you run it in a file or in a console.
Also, try to migrate to let and const. This is extra confusing because var behaves differently in global scope and elsewhere. In console and in browser, your outer var x is in global scope, so it defines a global variable (window.x and global.x). In a Node file, var x is not in a global scope, so it does what it normally does when not in global scope: defines a local variable x (not this.x, not global.x, just x). Thus, you have two local variables, the inner one shadowing the outer one, which makes the outer one inaccessible. Meanwhile, this.x has never been defined.

In chrome this is a object of Window as if you do this.constructor.name you get Window as a constructor name so while accessing this.x it will look for the global variable x and does not reference to the function scope.
var x = 1;
var fn = function (a) {
var x = a;
console.log(`local x = ${x}`);
console.log('Constructor ', this.constructor.name);
console.log(`global x = ${this.x}`);
}
fn(10);
However, in NodeJS, this will always refer to the function prototype (not the global scope). Thus, you do not have any value x associated with the prototype of the function so it gives you undefined.

Related

Scope chain and global object in javascript

When we declare a variable with the var keyword in the global scope var x = 10;, a property with the same name is created in the global object (window, global, self, globalThis, depending on the environment). So, here is my question:
If I try to access that variable console.log(x) js will look for it into my declared code first to see if its there or it will jump directly to the global object? I know that if I do this:
let myVar = 20;
globalThis.myVar = 30;
console.log(myVar) // 20, so my let declaration is readed first.
But what happens with var declarations?
In browser children of window object are directly accessible by their names without explicit window. when you create a local variable however you shadow the name even if exists under window so yes local will be accessed first
In programming this is called variable shadowing you can read more on the wiki I linked
PS. If you are on global scope and use var it will be as if you declared the thing under window itself I will demonstrate this with a snippet
var foo = 12;
console.log(window.foo)//12
window.foo=10
console.log(foo)//10
//However if you use let or const this will not happen

Javascript variable capture

I faced with strange behaviour when using eval in JS.
var f = function () {
var x = 10;
return function () {
eval('console.log(x);');
window['eval']('console.log(x);');
}
};
f()();
OUTPUT:
10
undefined:1
console.log(x);
^
ReferenceError: x is not defined
Why using eval explicitly captures the x but global['eval'] doesn't?
And even though global['eval'] doesn't capture x, why it's unable to see after eval, which already captured x?
window['eval'] operates at global scope, eval() operates at local scope.
From Mozilla's Javascript reference:
If you use the eval function indirectly, by invoking it via a
reference other than eval, as of ECMAScript 5 it works at global scope
rather than local scope; this means, for instance, that function
declarations create global functions, and that the code being
evaluated doesn't have access to local variables within the scope
where it's being called.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
Your inner function does not actually capture the reference of x, and so it is never directly passed to eval.
eval usually works at the local scope and so the first call succeeds (because the local scope contains the declaration of x).
However, if you invoke eval in such a way that you don't have a direct reference to it, it will invoke itself in the global scope, which var x is not a part of, and it fails.
Just don't use eval.
You can use Function.prototype.bind() to pass x to returned function
var f = function () {
var x = 10;
function y(n) {
eval(`console.log(${n})`);
window["eval"](`console.log(${n})`);
}
return y.bind(this, x)
};
f()();
window.eval work in global scope.
var variable = 1;
(function(){
var variable = 100,
cmd = "++variable";
document.write(eval(cmd)+"\n"); // increment local var 100 and output 101
document.write(window.eval(cmd)+"\n"); // increment global var 1 and output 2
})();

Why does Chrome debugger get undefined when accessing variables in Closure? [duplicate]

This question already has answers here:
Why does Chrome debugger think closed local variable is undefined?
(7 answers)
Closed 4 years ago.
Code:
function test4() {
var x = 10;
var y = 100;
// inner referred x only
function inner () {
console.log(x);
debugger;
}
// inner2 referred y to make sure y is in the scope of inner
function inner2 () {
console.log(y);
}
return inner;
}
var foo = test4();
foo();
y is in the scope of inner even only inner2 which never been used refer to it. I checked the result in scope and x, y are there:
But when I checked variables in watch panel and console, I can't get all of them:
It's weird that y is in the scope but get not defined when using debugger.
So, is it means that debugger can not access variable that not used in current context even it's in the closure or it just a bug? (My chrome version is 51.0.2704.103 m)
It's similar to Why does Chrome debugger think closed local variable is undefined? but not the same. Because inner2 in my code make sure that y is in the closure. And actually my question is opposite to Louis's answer under that question.
You are a first-hand observer of the internal mechanics of scope-optimization. Scope optimization is checking to see which variables are used in the current scope and optimizing out access to unused variables. The reason for this is because in the machine code generated from JIT compilation of javascript, the whole concept of variable naming is lost. But, to maintain javascript compliance, the JIT compiler associates an array of used local variables to each javascript function. Observe the following code.
(function(){
"use strict";
var myVariable = NaN; // |Ref1|
var scopedOne = (function(){
var myVariable = 101; // |Ref2|
return x => x * myVariable;
})();
var scopedTwo = (function(){
var myVariable = -7; // |Ref3|
return x => x / myVariable;
})();
console.log("scopedOne(2): ", scopedOne(2));
console.log("scopedTwo(56): ", scopedTwo(56))
})();
As seen above, Javascript is a scoped stack-based language. If Javascript was not a scoped language, then the variables used in the functions would depend on the values of the variables at the location where the function was being executed. For instance, without a scope, scopedOne would use the value of myVariable at |Ref1| (NaN) instead of at |Ref2| (101) and would log NaN to the console. Back to the main point, in the machine code, when the debugger comes in, it can only figure out where the actual locations in memory are of the used variables since only those memory locations have persisted to machine code as only those variable have been used. The memory locations of the rest of the variables remain a mystery to it. As you have observed, this has the secondary side-effect of making unused variables in the scope "invisible" to that function. However, there is a solution.
To circumvent this problem, simply wrap the debugger; statement in an eval to force the browser to do an expensive variable lookup of all the variables in the scope. Basically, the browser has to go back to the original source code, examine it for the original names of the variables in the scope, and figure out where the values of the variables are stored by the JIT-generated machine code. Open up developer tools and run the snippet below. Then go to the prior level in the "Call Stack" panel and observe how the visibility of the value of the variable y changes from visible inside eval to invisible outside eval.
function test4() {
var x = 10;
var y = 100;
// inner referred x only
function inner () {
console.log(x);
eval("debugger;");
}
// inner2 referred y to make sure y is in the scope of inner
function inner2 () {
console.log(y);
}
return inner;
}
var foo = test4();
foo();

Is it possible to write a JS function with no access to global variables?

For better knowledge of what a function is using, etc.
Might also be faster for variable lookups if not accessing the global scope?
Suppose I have:
a = 5;
b = 5;
in the global scope. Is it possible to wrap the function below such that
function go() {
console.log(a);
}
would not have access to "a" and the global namespace and return
Uncaught ReferenceError: a is not defined
No, there is no way to completely prevent access to global variables. That said, you can provide it a different set of global variables: namely, run it in an iframe. This isn’t bulletproof, though, since it could then just use window.parent to access the global variables of the parent.
Yes. The example below is straight from MDN eval.
You could try this IF you could wrap your entire codebase in a single wrapper function so that all your objects and functions fall into local scope. (I am not sure how practicable this is but it works in Chrome and Firefox)
(function() {
var x = 2, y = 4;
function range(a,b){return [a,b];}
console.log("DIRECT", eval("x + y"), eval("range(3,4)")); // Direct call, uses local scope, result is 6
var geval = eval;
console.log("INDIRECT", geval("x + y"), geval("range(3,4)")); // Indirect call, uses global scope, throws ReferenceError because `x` is undefined
})()
I believe that no matter what the current scope is, there is always a way to get to the global object:
let ref_to_global = (function(){
return this;
}).call(null);
Then one can access any property of the global object directly:
let value = ref_to_global["a"];
This means there is no way to make global scope inaccessible, if that was a question.

if a function's lexical environment is created at the time the function is *defined*, then why can a free variable be declared *after* the function?

EDIT: thanks for the answers, I think I get it now. It requires an understanding of scope and hoisting. Below is a new example that I think illustrates both well:
var a = function (){
alert(x);
}
var x = 1;
(function(){
var x = 2;
a();
})();
The above alerts 1. Lexical scope is illustrated by the fact that this alerts 1 and not 2, and hoisting is illustrated by the fact that the "var x = 1" line comes after the declaration of a and definition of the anonymous function with the "alert(x)". Allegedly, hoisting means that the above is equivalent to the below (source: http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html):
var x;
var a = function (){
alert(x);
}
x = 1;
(function(){
var x = 2;
a();
})();
So since x is effectively initialized before the function definition, the x in "alert(x)" is the same x that subsequently gets set to 1.
Since JS uses lexical scope, the line "var x = 2" does not override the x associated with a.
Is that about right?
---original question---
I've spent pretty much all day trying to figure this out. I've read a few articles on scope and closures in Javascript, but this still eludes me.
I'm told that a function's lexical environment is created when the function is defined, not when it is executed.
So if there is no variable called x declared anywhere in my program, then what is the closure environment for the anonymous function that a points to when I do this?:
var a = func(){
var y = 7; //just for illustrative purposes
alert(x);
});
My understanding is that an environment is a mapping of variable names to values...so it would be
y: 7
x: ?
Is that correct? The following code alerts "10":
(function (){
var a = function(){
alert(x);
};
var x = 10;
a();
})();
My guess would be that when a is called, the JS checks the closure environment for a mapping for x and finds none, and subsequently checks the local environment and finds the x set to 10 by "var x = 10". Is that right?
If that were the case, then I would expect the following would also work, and yet it does not:
var a = function(){
alert(x);
};
(function (){
var x = 10;
a();
})();
What I would expect to happen is that, when a is executed, and "alert(x);" is run, it checks the closure environment for x, finds none, then checks the outer environment where "var x = 10" is and finds that x. But instead I get an error that x isn't defined.
Maybe it would help if I could know what 'preparatory' work is done by the interpreter when the anonymous function that a is set to is initially defined--i.e. when the interpreter encounters the x in "alert(x)".
Thanks
the JS checks the closure environment for a mapping for x and finds none
In fact it does find one. The position of the var statement does not matter, the name x is still bound to the closure of the outer function. You can think of the declarations being collected during the parsing step and moved to the front (this process is called hoisting).
Thr process of walking up the scopes surrounding an expression in the source code is called lexical binding.
For your second example to work you'd need dynamic binding, which JS does not support (except for globals).
Think of closure as a stack. Each function has a map/hash in that stack. Here's an example where each level has a value of x that is overwritten by a sub-closure.
function a(){
var x = 2;
function b(){
var x = 3;
function c(){
var x = 4;
}
}
}
When referencing a value of x, the js processor looks in the current closure. Finding undefined, it walks up the closure stack attempting to find a value. Only when reaching the top level or 'global' scope does it give up and give the value of 'undefined'.
Your example here:
var a = function(){
alert(x); //no value of x in this closure
};
//no value of x in this closure either
(function (){
var x = 10; //this is a completely separate stack
a(); //no value of 'a' in this closure, but there is one in global scope
})();
I can see the confusion on the 'a' function not knowing about the 'x' in the anonymous function. Closure scopes are defined 'as written' not 'as called'. You can't inject or mess with variables in a functions closure scope from outside that closure scope. It is one of the things about js that drives me crazy and that I wish were different. I'd so love to have a set of keywords for messing with closure scope...

Categories

Resources