How to create a new variable in a closure - javascript

Consider this code
'use strict';
var factory = () => script => eval(script);
var closure = factory();
closure('var v = 0');
var val = closure('typeof v');
console.log(val);
This is my attempt to achieve it. I want to create a closure and then to allow users to create a new local variable in that closure. Is that even possible?
I somewhere read that "native function 'eval' can even create a new variable in the local execution context.". So, why doesn't it work in my example? My guess is that it is because the function finished executin and number of variables after function ends cannot be changed, but I am not sure.
My example script creates a closure and tries to declare and initialize new variable v in that closure and assign number 0 to it. I expected that the result of typeof v should be number, but it is actually undefined.
So, I have two questions:
Why doesn't it creates the variable v as expected
How to actually achieve that (what would be the working example)?

Well it is locally scoped, but a bit too local
var factory = () =>{
return script =>{
//this is the scope. you cant access any variables outside of this.
eval(script);
};
//you want to eval here, which is impossible
};
You could do very hacky scope stuff to work around this ( store all variables in context):
var factory = (context={}) => script =>{ with(context){ return eval(script); }};
You need to initialize all local variables on creation:
var exec=factory({v:0, b:undefined});// note that you need to set a value explicitly {v,b} wont worm
and then it works as expected:
console.log(
exec("v"),//0
exec("v=2"),//2
exec("v"),//2
typeof v //undefined
);
http://jsbin.com/xibozurizi/edit?console
If you dont want to go that deep, the only thing you could do would be concatenating the strings:
var factory = code => concat => (eval(code),res=eval(concat),code+=concat,res);
// or shorter / more buggy
var factory = code => concat => eval(code+=";"+concat);
var exec=factory("var a=1;");
console.log(
exec("a;"),//1
exec("var b=a+1"),
exec("b"),//2
tyepof a, typeof b //undefined
);
http://jsbin.com/midawahobi/edit?console
The upper code will run the strings multiple times, which is may not wanted. Another approach:
var factory=code=>({
code,
run(c){
return eval(this.code+";"+c);
},
add(c){ this.code+=";"+c}
});
So you can do
var exec=factory("var a='hello'");
exec.run("alert(a)")//alerts hello
exec.add("var b=a+' world'");
console.log(exec.code,exec.run("b"));//hello world, the upper alert isnt run again
http://jsbin.com/lihezuwaxo/edit?console
note that evaling is always a bad idea...

Your call to eval() does create a new variable, and it does so in the local execution context of the call. That context is inside that function, so it doesn't effect the context outside, where you call closure().
Generally eval() is to be strictly avoided. It makes optimization hard (or impossible) so functions that use it will be ignored by the optimizer. There are some rare circumstances in which it's useful, but declaring a new local variable doesn't seem like one of them.

You can do like that
var factory = () => script => eval(script);
var closure = factory();
var v;
closure('v=20')

Related

How to access variable inside JavaScript anonymous function

How can I access variable C?
test = function(){
a = 1;
b = 2;
c = a+b;
return c
}
console.log(c)
Returns:
Uncaught ReferenceError: c is not defined
EDIT: Please disregard above question
To elaborate more on this. I am new to JavaScript and am stuck on a block of code that I can't get to work. I tried to simplify the problem I was having but I think that may have backfired...
Here is the full code that I am having trouble with. I need to access the variables inside of getAPIData.onload, But am getting "undefined"
What is the best way to access these values?
getData();
function getData(){
var activeMachines = [41, 44, 45]
var dict = {};
let data = activeMachines.reduce((obj, machineID) => {
var getAPIData = new XMLHttpRequest();
var url = 'http://127.0.0.1:8000/processes/apidata/' + machineID + '/';
getAPIData.open('GET', url);
getAPIData.send();
getAPIData.onload = function(){
var APIData = JSON.parse(getAPIData.responseText);
dict['temp' + machineID] = APIData[0].tempData;
dict['humid' + machineID] = APIData[0].humidData;
timeValue = String((APIData[0].dateTime));
dict['time' + machineID] = new Date(timeValue);
}
temp = dict['temp'+ machineID];
humidity = dict['humid'+ machineID];
time = dict['time'+ machineID];
obj['machine_'+ machineID] = {temp, humidity, time}
return obj
}, {})
console.log(data);
}
This returns the data dictionary, but all of the values are undefined.
Any help is appreciated.
I will read more into JavaScript scope as others have suggested.
c is a global variable that is created by calling the anonymous function assigned to the global variable test. However, you never call the anonymous function assigned to test, therefore the variable never gets created.
So, the obvious solution is to just call the anonymous function assigned to test:
test();
console.log(c);
// 3
However, this is a really bad idea. Global variables are evil, evil, evil. They tie together disparate parts of your program into a giant tangled mess: since they are accessible from everywhere, you have to assume that they may be accessed from everywhere. And changing them in one place may change the behavior of some completely unrelated code in a completely different part of your code.
Actually, it is even worse: since they are global, changing them in one place may not only change the behavior of some completely unrelated code in a completely different part of your code, it may even change the behavior of some completely unrelated code in a completely separate part of someone else's code!
The only way to avoid this, would be to invent unique names for every global variable, otherwise they are going to clash. In fact, you would need to invent a unique name for every global variable that was ever written, is written, and will be written by every programmer in the universe for all eternity!
But actually, there is no need for global variables here at all, since you actually return the value of c in your anonymous function. You can make those variables local to your function and simply use the return value. In fact, you don't even need variables at all, since you never "vary" them, you can make them constants instead.
Note: in modern ECMAScript, you should always prefer consts (unless you have a very good reason to use a let) and never use var or even worse (like you are doing here) implicit global variable definition. So, your code, in modern ECMAScript, should look like this:
const test = function () {
const a = 1,
b = 2,
c = a + b;
return c;
}
console.log(test());
Also, since you don't give your function a name and don't rely on dynamic scoping of this, you can use arrow function literal syntax:
const test = () => {
const a = 1,
b = 2,
c = a + b;
return c;
}
console.log(test());
or just give your function a name:
function test() {
const a = 1,
b = 2,
c = a + b;
return c;
}
console.log(test());
the variables A B and C are of use locally within the function, but it returns the value of C, with just calling the function returns the value, as it says - charlietfl console.log(test()), you can call it that way or:
var c = test ();
alert(c) or console.log (c);
Now you can use the name varibale "c" outside the function.

Is there a simple method of turning a global var into a local var?

Let's say we have a function that looks like this:
const fn = () => x;
This function should return the value of x where x is available in the global scope. Initially this is undefined but if we define x:
const x = 42;
Then we can expect fn to return 42.
Now let's say we wanted to render fn as a string. In JavaScript we have toString for this purpose. However let's also say we wanted to eventually execute fn in a new context (i.e. using eval) and so any global references it uses should be internalized either before or during our call to toString.
How can we make x a local variable whose value reflects the global value of x at the time we convert fn to a string? Assume we cannot know x is named x. That said we can assume the variables are contained in the same module.
If you want lock certain variables while converting function to string, you have to pass that variables along the stringified function.
It could be implemented like this (written with types -- typescript notation)
const prepareForEval =
(fn: Function, variablesToLock: { [varName: string]: any }): string => {
const stringifiedVariables = Object.keys(variablesToLock)
.map(varName => `var ${varName}=${JSON.stringify(variablesToLock[varName])};`);
return stringifiedVariables.join("") + fn.toString();
}
Then use it like this
const stringifiedFunction = prepareForEval(someFunction, { x: x, y: y })
// you can even simplify declaration of object, in ES6 you simply write
const stringifiedFunction = prepareForEval(someFunction, { x, y })
// all variables you write into curly braces will be stringified
// and therefor "locked" in time you call prepareForEval()
Any eval will declare stringified variables and funtion in place, where it was executed. This could be problem, you might redeclare some variable to new, unknown value, you must know the name of stringified function to be able to call it or it can produce an error, if you redeclare already declared const variable.
To overcome that issue, you shall implement the stringified function as immediatelly executed anonymous function with its own scope, like
const prepareForEval =
(fn: Function, variablesToLock: { [varName: string]: any }): string => {
const stringifiedVariables = Object.keys(variablesToLock)
.map(varName => `var ${varName}=${JSON.stringify(variablesToLock[varName])};`);
return `
var ${fn.name} = (function() {
${stringifiedVariables.join("")}
return ${fn.toString()};
)();
`;
}
this modification will declare function and variables in separate scope and then it will assign that function to fn.name constant. The variables will not polute the scope, where you eval, it will just declare new fn.name variable and this new variable will be set to deserialized function.
We cannot know x is named x. This is the central piece of this puzzle and is therefore bolded in the original question. While it would be nice if we had a simpler solution, it does seem a proper answer here comes down to implementing some kind of parser or AST traversal.
Why is this necessary? While we can make the assumption that x lives in a module as a global (it's necessarily shared between functions), we cannot assume it has a known name. So then we need some way of extracting x (or all globals really) from our module and then providing it as context when we eventually eval.
N.B.: providing known variables as context is trivial. Several answers here seem to assume that's a difficult problem but in fact it's quite easy to do with eval; simply prepend the context as a string.
So then what's the correct answer here? If we were to use an AST (Acorn may be a viable starting point, for instance) we could examine the module and programmatically extract all the globals therein. This includes x or any other variable that might be shared between our functions; we can even inspect the functions to determine which variables are necessary for their execution.
Again the hope in asking this question originally was to distill a simpler solution or uncover prior art that might be adapted to fit our needs. Ultimately my answer and the answer I'm accepting comes down to the nontrivial task of parsing and extracting globals from a JavaScript module; there doesn't appear to be a simple way. I think this is a fair answer if not a practical one for us to implement today. (We will however address this later as our project grows.)
You can use OR operator || to concatenate current value of x to fn.toString() call
const fn = () => x;
const x = 42;
const _fn = `${fn.toString()} || ${x}`;
console.log(_fn, eval(_fn)());
Global variables can be made local (private) with closures. w3Schools
function myFunction() {
var a = 4;
return a * a;
}
Thanks to guest271314, I now see what you want.
This is his code, just little improved:
const stringifiedFn = `
(function() {
const _a = (${fn.toString()})();
return _a !== undefined ? _a : ${JSON.stringify(fn())};
})();
`;
this code will execute fn in context, where you eval, and if fn in that context returns undefined, it returns the output of fn in context, where it was stringified.
All credit goes to guest271314
do you mean this? only answer can post code, so I use answer
var x = 42
function fn() {
return x
}
(() => {
var x = 56
var localFn = eval('"use strict";(' + fn.toString()+')')
console.log(localFn)
console.log(localFn())
})()
why rename to localFn, if you use var fn=xx in this scope the outer fn never exists!
in nodejs? refer nodejs vm
passing context? you can not save js context unless you maintain your own scope like angularjs
If you're already "going there" by using eval() to execute fn() in the new context, then why not define the function itself using eval()?
eval('const fn = () => ' + x + ';')

Function call - this == Window and not undefined

Ok guys. I have this basic code which returns a jQuery-esque function called Q.
q=new function(){
var u,q;
q=function Q(slf){
console.log('slf ⇒',slf);
ex=q_ex.call(slf);
console.log('ex ⇒',ex);
};
return q;
function q_ex(){
console.log('this ⇒',this);
return (this!==u && this!==null);
};
};
If I was to call q();. The resulting console display would be:
slf ⇒ undefined
this ⇒ Window {external: Object, chrome: Object, document: document, speechSynthesis: SpeechSynthesis, caches: CacheStorage…}
ex ⇒ true
Now the astute among you will realize that I require this to be undefined and not Window!
Am I missing something here, or does this default to Window if it has the value null or undefined?
I was able to get something working by changing some code around:
var q = (function() {
'use strict';
var u;
function q_ex() {
console.log('this ⇒',this);
return (this!==u && this!==null);
}
return function Q(slf) {
console.log('slf ⇒',slf);
var ex = q_ex.call(slf);
console.log('ex ⇒',ex);
}
})();
q({ foo: 'bar' });
q();
Yes, this tends to default to Window, hence why using new is dangerous. The answer to why you get undefined when printing slf needs a bit more code samples to explain properly, I'll do that when I get home as I'm on my phone at the moment
Edits:
First off I made a bunch of comments to your original code to highlight some problems I found
// Dont use new, its not needed here, use closures
// Also q is not declared here so it will become a global variable, bad bad bad
q=new function(){
// Both declarations are unneeded as you can just return q straight away
// and u never gets a value assigned to it so why declare it?
// If you want to compare with undefined then just write this !== undefined
// Also, refrain from using the same names in nested structures if you value your sanity
var u,q;
// Use either q = function(...){...} or function Q(...){...}
q=function Q(slf){
console.log('slf ⇒',slf);
// ex is not declared with var so this will create a global variable, bad bad bad
ex=q_ex.call(slf);
console.log('ex ⇒',ex);
};
// No need to write the function and then return it.
// Use return(function(...){...}) instead.
return q;
// Just a personal preference but I'd put this before the return
// statement to make it more readable and also use q_ex = function(...){...}
function q_ex(){
console.log('this ⇒',this);
return (this!==u && this!==null);
};
};
Below is how I'd write it
var q = (function(){ // Closure
var q_ex; // Declarations
q_ex = function() { // Internal function
console.log('this ⇒', this);
return (this!==undefined && this!==null);
};
return(function(slf) { // Returned function
console.log('slf ⇒', slf);
var ex = q_ex.call(slf);
console.log('ex ⇒', ex);
});
})(); // Call in the end to get returned function
q() // Returns the same as yours did
q(5) // A numeric object with the value of 5 is now set as 'this'
The way you wrote yours seems to work exactly the same as mine but I cant exactly understand it. Could just be that I'm too deep in the closure religion.
Anyhow, the problem that you had stems from the fact that you run q(), with no parameters. If you ran q(5) you'd see that this becomes 5. This is because if the this parameter is not provided then it defaults to Window. This is the reason new is often dangerous. Say you wrote an object that uses this inside it to store values and thus requires to be created with new. Now if someone created it and forgot to use new you end up polluting the global namespace by attaching all your variables to the Window object (Window === global in browser).
A good way around that risk is to write your objects using closures, that way, how ever they are created they still work the same. You can still access the variables you want from your closing function by just using their name or if you want to group them up somehow just create a single "master" object in your closing function and add everything else into that.
Also, 'use strict' basically disallows some bad practices. Read up on it and use it, makes life easier. Also, use jsHint or any other JavaScript quality checker to help you keep your code clean.
Hope that helped.
Ok guys for anyone watching, I have found my answer. But first, I will clarify a little on the code...
q=new function(){
The reason for using new function is that I had never really used/practiced with self-executing - anonymous functions before and this was the only way I knew how!
q(x) === function Q(x){...}
q is purposefully global!
u is shorthand for undefined, var u === var u=undefined..
It's true I can write this !== undefined, but q_ex() will be used, in future, for multiple object types, from strings to numbers etc...
This level of 'same-name variables' is quite acceptable for it's purpose!
q=function Q(slf){
The reason behind naming the function is purely astetic, and is generally removed once coding is tested.
ex=q_ex.call(slf);
// ex is not declared with var so this will create a global variable, bad bad bad
Well spotted! Though this is a some-what simplified version of the code that is causing the problem, as such ex doesn't need declaring here for the problem to arise.
One last thing... I understand there seems to be no reason for the outer casing of the 'Q' function, but if you look closer... My only global variable is 'q'! If not for the outer casing: u, q, q_ex and any future object would be global and this is NOT the requisite.
Ok then, for my answer...
'use strict';
Function.prototype.call
Strict mode
The solution to my woes...
var q=(function(){
var u,q;
q=function Q(slf){
console.log('slf ⇒',slf);
var ex=q_ex.call(slf);
console.log('ex ⇒',ex);
};
return q;
function q_ex(){
'use strict';
console.log('this ⇒',this);
return (this!==u && this!==null);
};
})();
q();
slf ⇒ undefined
this ⇒ undefined
ex ⇒ false
Thanks for all your help guys, I can now move on with the bigger picture!
I will try to pull myself away from using new function and replace it with (function(){})().
Hope this helps.

Should I use window.variable or var?

We have a lot of setup JS code that defines panels, buttons, etc that will be used in many other JS files.
Typically, we do something like:
grid.js
var myGrid = .....
combos.js
var myCombo = .....
Then, in our application code, we:
application.js
function blah() {
myGrid.someMethod()
}
someother.js
function foo() {
myCombo.someMethod();
myGrid.someMethod();
}
So, should we be using the var myGrid or is better to use window.myGrid
What's the difference?
A potentially important difference in functionality is that window.myGrid can be deleted, and var myGrid can not.
var test1 = 'value';
window.test2 = 'value';
console.log( delete window.test1 ); // false ( was not deleted )
console.log( delete window.test2 ); // true ( was deleted )
console.log( test1 ); // 'value' ( still accessible )
console.log( test2 ); // ReferenceError ( no longer exists )
I would suggest creating a namespace variable var App = {};
App.myGrid = ...
That way you can limit the pollution of the global namespace.
EDIT: Regarding the number of variables issue - 2 possible solutions come to mind:
You can further namespace them by type(Grids, Buttons, etc) or by relationship(ClientInfoSection, AddressSection, etc)
You encapsulate your methods in objects that get instantiated with the components you have
ex: you have
function foo() {
myCombo.someMethod();
myGrid.someMethod();
}
becomes:
var Foo = function(combo, grid) {
var myCombo = combo;//will be a private property
this.myGrid = grid;//will be a public property
this.foo = function() {//public method
myCombo.someMethod();
myGrid.someMethod();
}
}
App.myFoo = new Foo(someCombo, someGrid);
App.myFoo.foo();
this way you limit the amount of little objects and only expose what you need (namely the foo function)
PS: if you need to expose the internal components then add them to this inside the constructor function
One nice use of window.variable is that you can check it without having a javascript error. For example, if you have:
if (myVar) {
//do work
}
and myVar is not defined anywhere on the page, you will get a javascript error. However:
if (window.myVar) {
//do work
}
gives no error, and works as one would expect.
var myVar = 'test' and window.myVar = 'test' are roughly equivalent.
Aside from that, as other said, you should descend from one global object to avoid polluting the global namespace.
In global scope the two are in fact equivalent functionality-wise. In function scope, var is certainly preferable when the behaviour of closures is desired.
I would just use var all of the time: firstly, it's consistent with the usually preferred behaviour in closures (so it's easier to move your code into a closure if you decide to do so later), and secondly, it just feels more semantic to me to say that I'm creating a variable than attaching a property of the window. But it's mostly style at this point.
The general answer to the question would be to use var.
More specifically, always put your code in an Immediately Invoked Function Expression (IIFE):
(function(){
var foo,
bar;
...code...
})();
This keeps variables like foo and bar from polluting the global namespace. Then, when you explicitly want a variable to be on the global object (typically window) you can write:
window.foo = foo;
JavaScript has functional scope, and it's really good to take full advantage of it. You wouldn't want your app to break just because some other programmer did something silly like overwrote your timer handle.
In addition to other answers, worth noting is that if you don't use var inside a function while declaring a variable, it leaks into global scope automatically making it a property of window object (or global scope).
To expand on what Liviu said, use:
App = (function() {
var exports = {};
/* code goes here, attach to exports to create Public API */
return exports;
})();
By doing that you can hide some of your implementation specific code, which you may not want exposed by using var's inside. However, you can access anything attached to the exports object.

Accessing "pseudo-globals" by their name as a string

I am now in the process of removing most globals from my code by enclosing everything in a function, turning the globals into "pseudo globals," that are all accessible from anywhere inside that function block.
(function(){
var g = 1;
var func f1 = function () { alert (g); }
var func f2= function () { f1(); }
})();
(technically this is only for my "release version", where I append all my files together into a single file and surround them with the above....my dev version still has typically one global per js file)
This all works great except for one thing...there is one important place where I need to access some of these "globals" by string name. Previously, I could have done this:
var name = "g";
alert (window[name]);
and it did the same as
alert(g);
Now -- from inside the block -- I would like to do the same, on my pseudo-globals. But I can't, since they are no longer members of any parent object ("window"), even though are in scope.
Any way to access them by string?
Thanks...
Basically no, as answered indirectly by this question: Javascript equivalent of Python's locals()?
Your only real option would be to use eval, which is usually not a good or even safe idea, as described in this question: Why is using the JavaScript eval function a bad idea?
If the string name of those variables really and truly is defined in a safe way (e.g. not through user-input or anything), then I would recommend just using eval. Just be sure to think really long and hard about this and whether there is not perhaps a better way to do this.
You can name the function you are using to wrap the entire code.
Then set the "global" variable as a member of that function (remember functions are objects in JavaScript).
Then, you can access the variable exactly as you did before....just use the name of the function instead of "window".
It would look something like this:
var myApp = new (function myApp(){
this.g = "world";
//in the same scope
alert ( "Hello " + this["g"]);
})();
//outside
alert ( "Hello " + myApp["g"]);
if you want to access something in a global scope, you have to put something out there. in your case it's probably an object which references your closed off function.
var obj1 = new (function(){
var g = 1;
var func f1 = function () { alert (g); }
var func f2= function () { f1(); }
})();
you can add a method or property as a getter for g. if the value of g isn't constant you might do like
this.getG = function() { return g; };
you can work from there to access items by name, like
alert( obj1["getG"]() );
alert( window["obj1"]["getG"]() );

Categories

Resources