Javascript variable capture - javascript

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
})();

Related

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

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.

Access variable in if block

var func = function () {
var i: number = 0;
if (i == 0) {
var y: number = 1;
}
document.body.innerHTML = y.toString(); // js/ts should complain me in this line
};
func(); // output: 1
As you can see, I've declared variable y inside if block. So, I think it couldn't be referenced outside the scope.
But, when I've tried to run the code, the output is 1.
Is it an issue in typescript/javascript?
Variables in Javascript are hoisted, meaning that var y is moved to the top of the function as if it were declared there.
Initialization, however is not hoisted, so if you change i to be something other than 0, the variable y will still exist, but it will have the value undefined.
This means that the function is exactly equivalent to:
var func = function () {
var i: number = 0;
var y: number;
if (i == 0) {
y = 1;
}
document.body.innerHTML = y.toString(); // js/ts should complain me in this line
};
To get the behavior you expect, you need to use let, which is a part of ECMAScript 2015 (ES6). It is block scoped, as you expect. It will also effectively work so that it is accessible only from the point of definition onwards, which is also probably as you would expect.
If you re-declare a JavaScript variable, it will not lose its value.
The second reference might pave way for a new variable syntax. Actually if you recall variable declaration is not neccessary in javascript. Simpe
y=1;
also works.
The second time when you reference y, outside if block, in my opinion, it tries a re-declaration and retains the old value.
Reference - http://www.w3schools.com/js/js_variables.asp
& http://www.w3schools.com/js/js_scope.asp
Javascript has function scope afaik. Any variable declared within a function, should be accessible from anywhere within the function. So, if you have a function checking if i==0, then you can achieve what you are trying to achieve.
This is as it is supposed to be. Javascript scopes by function, not by block. (This does make it unlike many popular languages)
Variables declared like var myVar = 10; will seep into nested functions but not the other way around. Variables declared like myVar = 10; will go global.
I couldn't find anything which suggested that typescript was any different.
Variables declared inside of an if statement are not scoped to the if statement. They're scoped to the current execution context. There's the Global execution context and then when a function is run, it creates it's own execution context. Inside of your function, you created the variables y and i. It doesn't matter that y was created inside of the if statement, because once it runs, y is created in the scope of the function. So then you do y.toString(), which can access y because it's scoped to the function not the if statement. That's why you get the output of 1. This is not an error, it's by design.

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.

Difference between function expression in global scope and function declaration

I'm puzzled by something in my js. Normally I define functions like this:
function f(){
// do stuff
}
but I can also define functions like this:
f = function(){
// do stuff
}
I always thought there is no difference between them, but I now found that this is working:
f = function(){
alert('IT WORKS!!');
}
function createCallback(request){
request.done(function(data){
var html = '';
data['result'].forEach(function(bill){
html += "<tr onclick=\"f();\"><td>" + bill.title + "</td></tr>";
});
$("#someId").html(html);
});
}
but when I define f as follows:
function f(){
alert('IT WORKS!!');
}
and I click on the row, it gives a ReferenceError: f is not defined.
So I wonder: what is actually the difference between function f(){} and f = function(){}?
When you define a function without using var statement, by default the function will be defined as a property in the global scope.
Quoting MDN documentation on var,
Assigning a value to an undeclared variable implicitly creates it as a global variable (it becomes a property of the global object) when the assignment is executed. The differences between declared and undeclared variables are:
Declared variables are constrained in the execution context in which they are declared. Undeclared variables are always global.
Declared variables are created before any code is executed. Undeclared variables do not exist until the code assigning to them is executed.
Declared variables are a non-configurable property of their execution context (function or global). Undeclared variables are configurable (e.g. can be deleted).
Because of these three differences, failure to declare variables will very likely lead to unexpected results. Thus it is recommended to always declare variables, regardless of whether they are in a function or global scope. And in ECMAScript 5 strict mode, assigning to an undeclared variable throws an error.
So, when you define with function function_name(...){...} syntax, it will be in the current scope.
Since the second function definition is in the global scope tr's onclick can find f. Try using var statement like this
var f = function(){
alert('IT WORKS!!');
}
you will get the same ReferenceError: f is not defined.
You forgot the var statement. The function is defined globally when using f = function(){ }. This is why it’s accessible from the onclick handler and the other is not.
Please also read var functionName = function() {} vs function functionName() {} as suggested by #Nehal.

Javascript Global vs Local Scope

I'm currently trying to understand Javascript's interesting take on global vs local scope.
My question is that why would the following returns undefined?
var a = 100;
function local() {
if (false) {
var a = 50;
}
alert(a);
}
Additionally, I ran this:
var a = 100;
function local() {
alert(a);
}
The result was a resounding: 100.
Is there some way that we can specify when we want the variable to be taken from the global scope, like a PHP global keyword, that we can use with Javascript?
Because of hoisting.
It means every variable declaration get's popped to the top of the function so the actual code that runs is:
var a = 100;
function local(){
var a;
if (false)
a = 50;
alert(a);
}
It has very little to do with global VS. local, you simply hid the outer a variable with the inner a variable.
Is there some way that we can specify when we want the variable to be taken from the global scope, like a PHP global keyword, that we can use with Javascript?
No
Regarding the question in the comment "In the case that I want to revert to using a global variable in case the if condition fails (which I intentionally did), how can I do so? ":
You can use eval:
var a = 100;
function local() {
if (false) {
eval('var a = 50;');
}
alert(a);
}
Live DEMO
But you should not!
JavaScript has function scope, not block scope (as many other programming languages). That means regardless where the var statement occurs (in your case, an if-branch that is not even executed), it declares a variable a in the function's local scope. It shadows the global a variable, but since it has no value assigned it returns undefined.
Is there some way that we can specify when we want the variable to be taken from the global scope, like a PHP global keyword, that we can use with Javascript?
You can access the variable as a property of the global object (window in browser environments):
var a = 100; // global
function loc() {
var a = 50;
alert(window.a); // 100
alert(a); // 50
}
loc();
However, you will hardly use this. Global variables are to be avoided in general, and in case of name clashes you should simply rename your local variable (for readability and maintainability at least).
I know that if a variable is in defined inside a function using var, then it will have Local Scope.
I also know that any function has easy access to Global variable.
When I tested following in Console of Browser, first output was undefined.
It means that function nwf() was unable to access Global variable nw1
This is due to reference error which highlights importance of Strict Mode
To avoid such scenario, never re-declare and/or initialize any variable with same name inside and outside any function, just unlike following:-
var nw1 = 1; // var creates global scope outside function
function nwf() {
alert(nw1);
var nw1 = 2; // var creates LOCAL scope inside function
alert(nw1);
};
nwf();

Categories

Resources