JS new Function - logging a variable by name dynamically - javascript

We are given a code snippet in run time along with a name of a variable used in code. We want to evaluate the given code and log the value of the variable.
Example:
suppose our code is var foo = "Blah"; and the name of the var is foo.
eval('var foo = "Blah"; let varName = "foo"; console.log(this[varName]);');
yields Blah.
yet
new Function('var foo = "Blah"; let varName = "foo"; console.log(this[varName]);')();
yields undefined.
Is there a way to make get this to work with new Function?

With new Function, you're creating a new function. The code that is executed looks something like:
function someFunction() {
var foo = "Blah"; let varName = "foo"; console.log(this); console.log(this[varName]);
}
someFunction();
There, foo is in local scope of the someFunction. It's not global, so variables declared with var inside the function don't get put onto the global object.
In contrast, eval runs on the top level. Your eval code that is executed looks something like:
var foo = "Blah"; let varName = "foo"; console.log(this[varName]);
which is all on the top level, so the foo variable that was declared gets put onto the global object, the this.

Substitute the variable name into the function definition directly, rather than using this.
let varName = 'foo';
new Function(`var ${varName} = "Blah"; console.log(${varName});`)();

Everything here is about Scope. Global Scope and Block Scope.
Global Scope
Variables declared Globally (outside any function) have Global Scope.
var bikeName = "Honda";
// code here can use bikeName
function myFunction() {
// code here can also use bikeName
}
and
var greeter = "hey hi";
function newFunction() {
var hello = "hello"; //Variables declared Locally (inside a function) have Function Scope.
}
console.log(hello); // error: hello is not defined
Here, greeter is globally scoped because it exists outside a function while hello is function scoped. So we cannot access the variable hello outside of a function.
Block Scope
A block is a chunk of code bounded by {}. A block lives in curly braces.
let times = 4;
if (times > 3) {
let hello = "say Hello instead";
console.log(hello);// "say Hello instead"
}
console.log(hello) // hello is not defined
You should read about them. Things will get clear
Hence in your case:
new Function('var foo = "Blah"; let varName = "foo"; console.log(this[varName]);')();
Everything inside function has function scope and will not be available global!

Related

Is there any mistake in new Function?

I have read the Function. So I try to transform function declaration into function expression by new Function. But, I am stuck in the code below:
function Foo(name, age) {
Foo.prototype.name = name;
Foo.prototype.age = age;
}
var foo = new Foo(1,2); // this is ok
console.log(Foo.prototype.hasOwnProperty('name'));
console.log(foo.hasOwnProperty('name'));
However there is an error after transformation:
var Foo = new Function(['name', 'age'], 'Foo.prototype.name=name;Foo.prototype.age=age;');
var foo = new Foo(1,2); // error: Foo is not defined
console.log(Foo.prototype.hasOwnProperty('name'));
console.log(foo.hasOwnProperty('name'));
Is there any mistake? Any answer is helpful.
The code is right in my chrome browser. However it makes an error on platform:
nodejs: 6.10.0
win7 64bit
The error is because var Foo isn't defined where the Function can reach it, at least not while using Node.
When you create a function from a string using the new Function() constructor, the function only has access to the global scope, regardless of where it's created.
And, when within a Node module, var Foo and var foo aren't global variables. They're local variables that exists only within the current file/module, within a "module" scope.
var bar = 'bar';
console.log(bar); // function
console.log(global.bar); // undefined
To allow the new Function() to access Foo, you'll have to define the latter as a global variable (or a property of the global object).
global.Foo = new Function(['name', 'age'], 'Foo.prototype.name=name;Foo.prototype.age=age;');
var foo = new Foo(1,2);
// ...
Though, you should be aware that modifying the global scope/object like this is generally frowned upon in Node, since this goes against its intent/design of using isolated modules.
Though, to define the function from an expression, you don't need to use the Function constructor. You can instead place the declaration where an Expression is expected, such as after an assignment operator.
var Foo = function (name, age) {
Foo.prototype.name = name;
Foo.prototype.age = age;
};
var foo = new Foo(1,2); // this is ok
// ...

Javascript- Variable Hoisting

This is a simple snippet, I just dont understand something.
The below code outputs 12, I understand that, because the var foo = 12; replaces the previous declaration of the variable.
<script>
var foo = 1;
function bar(){
if (!foo) {
var foo = 12;
}
alert(foo);
}
bar();
</script>
In the below code, it alerts 1 , which means the variable declared outside the function is accessible inside the function.
<script>
var foo = 1;
function bar(){
alert(foo);
}
bar();
</script>
But, in the below code, why it alerts undefined ?? I thought, it will alert 1, I am just assigning the previously declared variable to the new one.
<script>
var foo = 1;
function bar(){
if (!foo) {
var foo = foo;
}
alert(foo);
}
bar();
</script>
Variable declarations are pushed to the start of the function.
Therefore in reality the following is happening:
function bar(){
var foo;
if (!foo) {
foo = foo;
}
alert(foo);
}
Therefore you would need to change this to use window.foo so that you're referring to the global property rather than the function's property:
var foo = 1;
function bar(){
var foo;
if (!window.foo) {
foo = window.foo;
}
alert(foo);
}
bar();
Hoisting is slightly tricky. Function declarations are hoisted with the function assignment, but variable declarations are hoisted without the variable assignment. So the execution order of code is actually:
var foo;
var bar = function bar(){
var foo; // undefined
if (!foo) { // true
foo = foo; // foo = undefined
}
alert(foo);
}
foo = 1;
bar();
You could either use window.foo if you want to refer to the global variable foo, or better, just use a different variable name:
var foo = 1;
function bar(){
var baz = foo;
alert(baz);
}
bar();
The below code outputs 12, I understand that, because the var foo =
12; replaces the previous declaration of the variable.
var foo = 1;
function bar(){
if (!foo) {
var foo = 12;
}
alert(foo);
}
bar();
You are right because local variable overriding the global one.
In the below code, it alerts 1 , which means the variable declared
outside the function is accessible inside the function.
var foo = 1;
function bar(){
alert(foo);
}
bar();
You are correct. foo is declare in global scope so is accessible fron anywhere.
But, in the below code, why it alerts undefined ?? I thought, it will
alert 1, I am just assigning the previously declared variable to the
new one.
var foo = 1;
function bar(){
if (!foo) {
var foo = foo;
}
alert(foo);
}
bar();
This is a bit different. You are declaring a global variable and a local one with the same name. When your JavaScript program execution enters a new function, all the variables declared anywhere in the function are moved (or elevated, or hoisted) to the top of the function.
Another example:
var a = 123;
function f() {
var a; // same as: var a = undefined;
alert(a); // undefined
a = 1;
alert(a); // 1
}
f();
In javascript, until the ES5 specification, the scope is implemented only in terms of function body. The concept of block scope doesn't exist (really, will be implemented in the next javascript with the let keyword).
So, if you declare a variable var something; outside from function body, it will be global (in browsers global scope is the scope of the window object).
global variables
var something = 'Hi Man';
/**
* this is equal to:
**/
window.something = 'Hi Man';
If your code doesn't run in strict mode, there is another way to declare a global variable: omitting the var keyword. When the var keyword is omitted the variable belongs (or is moved) to the global scope.
example:
something = 'Hi Man';
/**
* this is equal to:
**/
function someFunction() {
something = 'Hi Man';
}
Local Variables
Because the non-existence of block scopes the only way to declare a local variable is to define it in a function body.
Example
var something = 'Hi Man'; //global
console.log('globalVariable', something);
function someFunction() {
var something = 'Hi Woman';
console.log('localVariable', something);
/**
* defining variable that doesn't exists in global scope
**/
var localSomething = 'Hi People';
console.log('another local variable', localSomething);
}
someFunction();
console.log('globalVariable after function execution', something);
try {
console.log('try to access a local variable from global scope', localSomething);
} catch(e) { console.error(e); }
As you can see in this example, local variables don't exist outside from their scope. This means another thing... If you declare, with the var keyword, the same variable in two different scopes you'll get two different variables not an override of the same variable (name) defined in the parent scope.
If you want to "override" the same variable in a child scope you have to use it without the var keyword. Because of the scope chain if a variable dosn't exist in a local scope it will be searched on their parent scope.
Example
function someFunction() {
something = 'Hi Woman';
}
var something = 'Hi Man';
console.log(1, 'something is', something);
someFunction();
console.log(1, 'something is', something);
Last thing, variable hoistment.
As I wrote below, at the moment, there isn't any way to declare a variable in some point of your code. It is always declared at the start of it scope.
Example
function someFunction() {
// doing something
// doing something else
var something = 'Hi Man';
}
/**
* Probably you expect that the something variable will be defined after the 'doing
* something else' task, but, as javascript works, it will be defined on top of it scope.
* So, the below snippet is equal to:
**/
function someFunction1() {
var something;
// doing something
// doing something else
something = 'Hi Man';
}
/**
* You can try these following examples:
*
* In the someFunction2 we try to access on a non-defined variable and this throws an
* error.
*
* In the someFunction3, instead, we don't get any error because the variable that we expect to define later will be hoisted and defined at the top, so, the log is a simple undefined log.
**/
function someFunction2() {
console.log(something);
};
function someFunction3() {
console.log('before declaration', something);
var something = 'Hi Man';
console.log('after declaration', something);
}
This happens because in javascript there are two different steps of a variable declaration:
Definition
Initialization
And the function3 example becomes as following:
function3Explained() {
var something; // define it as undefined, this is the same as doing var something = undefined;
// doing something;
// doing something else;
something = 'Hi Man';
}
IMHO it doesn't have anything to do with function declaration and hoisting ,
declaring the var with var inside function you are creating a variable in the function's isolated scope, this is why you get undefined.
var foo = 1;
function funcOne() {
var foo = foo;
alert('foo is ' + foo);
};
funcOne();
var bau = 1;
function funcTwo() {
bau = bau;
alert('bau is ' + bau);
};
funcTwo();
fiddle

Scope in JavaScript behaviour

This is a simple script that'll log the content of text from a Photoshop file.
var numOfLayers = app.activeDocument.layers.length;
var resultStr = "";
doAllLayers();
alert(resultStr);
function addToString(alayer)
{
if (alayer.kind == "LayerKind.TEXT")
{
var c = alayer.textItem.contents;
resultStr += c;
}
}
// main loop
function doAllLayers()
{
for (var i = numOfLayers -1; i >= 0; i--)
{
var thisLayer = app.activeDocument.layers[i];
addToString(thisLayer);
}
}
It works fine but I really should be passing a string into the function to add to itself, however it works.
How does the scope of JavaScript allow this to happen? Are declared local variables still accessed by functions or is it another trick I'm missing?
Here are some basic rules to variable scoping in JavaScript:
If defined with the var keyword, the variable is function-scoped. That is, the variable will be scoped to either the closest containing function, or to the global context if there is no containing function.
Example:
// globally-scoped variable (no containing function)
var foo = 'bar';
function test() {
// function-scoped variable
var foo2 = 'bar2';
if (true) {
// still function-scoped variable, regardless of the if-block
var foo3 = 'bar3';
}
// see?
console.log(foo3); // --> 'bar3'
}
If defined with the let keyword (ES6+), then the variable is block-scoped (this behavior more closely resembles most other C-family syntax languages). Example:
// error: top level "let" declarations aren't allowed
let foo = 'bar';
function test() {
// block-scoped variable (function are blocks, too)
let foo2 = 'bar2';
if (true) {
// block-scoped variable (notice the difference
// from the previous example?)
let foo3 = 'bar3';
}
// uh oh?
console.log(foo3); // --> ReferenceError: foo3 is not defined
}
If defined with neither the var or let keywords (e.g., foo = bar), then the variable is scoped to the global context. Example:
// globally-scoped variable
foo = 'bar';
function test() {
// also globally-scoped variable (no var or let declaration)
foo2 = 'bar2';
if (true) {
// still a globally-scoped variable
foo3 = 'bar3';
}
}
test();
console.log(foo, foo2, foo3); // 'bar', 'bar2', 'bar3'
In all of these cases, functions defined within scope of a variable still have access to the variable itself (technically you're creating a closure, as the numOfLayers and resultStr variables are lexically in scope of your addToString and doAllLayers functions).
Please note that the scoping rules are technically a little more nuanced than this, but you're better off reading a more in-depth article at that point.
You have defined var resultStr = ""; outside function which is a global variable. you can access this global variable inside addToString() and start concatenating to it. Note that inside the method you have not declared var resultStr = ""; else it would have been a different variable local to that method.

confusion of javascript closure

I have been proved I do not truly understand javascript closure, and I am being confused by the following codes. I thought fxn would access the outside foo, but it actually print out "underfined". Why??
var foo = "hello";
function fxn(){
alert(foo);
var foo = "test"
}
fxn();
This is because in JavaScript, variables get hoisted, which means
Variables are initialised to undefined when created. A variable with
an Initialiser is assigned the value of its AssignmentExpression when
the VariableStatement is executed, not when the variable is created.(ES5 ยง12.2)
Thus, semantically, your code would be equivalent, to the following...
var foo = "hello";
function fxn(){
var foo; //Variables are initialised to undefined when created
alert(foo);
foo = "test"; //A variable with an *Initialiser* is assigned the value of its *AssignmentExpression* when the *VariableStatement* is **executed**
}
fxn();
You define your variable foo outside your function. If you repeat calls for var, you redefine the variable inside the function and it loses its allocation.
Remove var in the function to access foo into the function fnx.
var foo = "hello";
function fxn(){
alert(foo);
foo = "test";
}
fxn();
Jsfiddle

Variable scope inside constructor

In JavaScript, functions can always access global variables. I have a class that I am using, and it references global variables. Here's a similar class:
function Test(){
this.abc = abc;
}
If I set the global abc then call this, it works.
var abc = 123,
testA = new Test;
console.log(testA.abc); // 123
But what if I don't want abc to be global? I wrapped the code in a function call, but I get an error saying abc is not defined.
(function(){
var abc = 123,
testA = new Test; // ERROR: abc is not defined
console.log(testA.abc);
})();
How can I read local variables inside a JavaScript constructor without adding variables to the global scope?
The problem is that local variables have lexical scope.
That means that to be resolved they must be within the same code block, or in enclosing code blocks.
Your code would only work if the definition of Test was also within the IIFE:
(function(){
var abc = 123,
testA = new Test; // ERROR: abc is undefined
function Test() { // this will be hoisted
this.abc = abc;
}
console.log(testA.abc);
})();

Categories

Resources