Variable variables in JavaScript - javascript

according to my knowledge, this feature already exists in PHP. lets look at the following php code:
$color = 'red';
$$color = 'dark';
description of the feature:
Sometimes it is convenient to be able to have variable variable names. That is, a variable name which can be set and used dynamically.A variable variable takes the value of a variable and treats that as the name of a variable. In the above example, red, can be used as the name of a variable.At this point two variables have been defined and stored in the PHP symbol tree: $color with contents "red" and $red with contents "dark".
my Question
can this be done in java Script?

Three different techniques come to mind, each with its warnings and (except the second one) uses:
1) You can declare a new variable in JavaScript anywhere in the code using the var keyword:
var $color = 'red';
The variable is actually defined throughout the scope in which the var occurs, even above the var statement — that is, these two functions are identical even though they look slightly different:
function foo() {
doSomething();
var x = 5;
x += doSomethingElse();
return x;
}
function foo() {
var x;
doSomething();
x = 5;
x += doSomethingElse();
return x;
}
This is because all vars take effect when the context for the function is created, not where they appear in the code. More: Poor, misunderstood var
2) If you just assign to a free symbol that's never been declared anywhere, you'll create an implicit global variable (not one constrained to the current scope), which is generally a bad idea. More: The Horror of Implicit Globals
3) Another thing you can do is have an object which is a container for various variables you want to track. You can create new properties on the object just by assigning them:
var data = {}; // A blank object
data.foo = "bar"; // Now `data` has a `foo` property
This technique is particularly handy when you need to track data that your script is completely unaware of, for instance based on user input, because you can use either dotted notation and a literal as above (data.foo), or you can use bracketed notation and a string (data["foo"]). In the latter case, the string can be the result of any expression, so all of these create a foo property on data:
// Dotted notation with a literal
data.foo = 42;
// Bracketed notation with a literal string
data["foo"] = 42;
// Bracketed notation with a string coming from a variable
s = "foo";
data[s] = 42;
// Bracketed notation with a string coming from an expression
s = "o";
data["f" + s + s] = 42;

var color = 'red';
window[color] = 'dark';
console.log(color, red);

Related

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 + ';')

Javascript global variable type change

In Javascript, I can assign a global variable in different data types in different function scope. For example:
var a = 1;
function foo(){
a = a + 10; //a is number in foo
}
function bar(){
a = a + "hello"; //a is string in bar
foo(); //a is string in foo now
}
foo();
bar();
My example is just a simple demo and I believe most Javascript programmers won't write it. However, is there any practical usage for such dynamic feature that global variable changes its data type in different functions?
Dynamic typing allows you to do stuff like this :
var a = false;
if(need to show msg 1){a="message 1"};
if(need to show msg 2){a="message 2"};
if(a){display(a);}
The example is not very good, but the idea is that you can use the same variable as a condition and content, or as an array element and as an error message if what you are looking for is not in the array,...
By the way, when you write a = 1, it is practically equivalent to window.a = 1; global variables can be considered as properties of the window object (.see here for precisions).
So when you write a = a + "hello";, a becomes a string everywhere and not just 'in foo'.
var a = 1; //global variable
In foo() if (1) is defined
function foo(){
a; //In this case a will be global variable and a = 1
}
function foo(){
var a; //In this case a will be private variable, a = undefined but global varibale a not change
}
It depends on what you're trying to do. With Javascript, global variables that are manipulated into different types can be dangerous if you're using a lot of callbacks. Callbacks functions triggering a lot (of course asynchronously) and manipulating a global variable can make it so you end up reading the wrong data in one or more callback functions and an unexpected result will follow deviating from your code's intention.

How to create dynamic variables using jquery?

I want some jquery variables to be created dynamically. In my code I am having a loop, and with the loop values I want to create some variables. Here is my sample code.
array=["student","parent","employee"]
$.each(user_types, function( index, value ){
var value+"_type" // this is the type of variable i want to build.
})
I have found about eval function. That code goes like this.
var type = "type"
eval("var pre_"+type+"= 'The value of dynamic variable, val';");
alert(pre_type) // this gives 'The value of dynamic variable, val' in alert box.
Is there any alternate ways as I have read the eval function is not prefered while coding .js files.
Any time you find yourself using a variable in the name of a variable, you probably want to use an object literal. Create the object with curly braces {}, and then set the object property key using square bracket notation:
var user_types = ["student","parent","employee"];
var types = {};
$.each(user_types, function( index, value ){
types[value] = 'The value of dynamic variable, val';
});
JSFiddle
Note: You haven't tagged it, but I assume because you've used each() that you are using jQuery, please correct me if I'm wrong.
First of all i must say that i can't think of any reason why you want to do this.
If you really need to have those variables, in global scope, you can do the following:
var array=["student","parent","employee"]
array.forEach(function(value){
window[value+"_type"] = 'My value ' + value;
});
console.log(student_type);
console.log(parent_type);
console.log(employee_type);
If you don't want the variables in global scope, i'm afraid i don't know an elegant solution.
I used array.forEach instead of your jQuery loop because the problem is not related to jQuery at all and because i don't think you said enough of your logic to make a coherent example.
EDIT: I should make it clear that while the 'variables' created behave mostly like other variables in global scope, they are NOT variables. Here is how they differ:
// Difference 1: hoisting
console.log(x); // undefined
console.log(y); // ReferenceError: y is not defined
var x = 5;
window[y] = 5;
console.log(x); // 5
console.log(y); // 5
// Difference 2: [[Configurable]]
delete x;
delete y;
console.log(x); // 5
console.log(y); // ReferenceError: y is not defined
If you want to add an intermediate variable inside the string, you can do it as follows:
var itemSelect: number = 1;
$(`#tab${this.itemSelect}-tab`).tab('show');
/* Result -> $(`#tab1-tab`).tab('show'); */
/* HTML */
<a id="tb1-tab"> </a>

This in Private context

I would like to access variables created inside a private context. I'm creating a private context like this:
(new Function("var a = 'hello'; console.log('this', this);")).call({});
// outputs -> this Object {}
I'm call'ing the function with an empty context. But this doesn't holds the a variable. And anyway how is it possible that console.log works with an empty context?
jsFiddle Demo
Inside of the scope for the function which creates the object var a is present. However, once that scope is lost, so is the variable. If you would like a to persist, you would need to attach it to the object that is created using the this keyword:
(new Function("this.a = 'hello'; console.log(this);")).call({});
However, this is an obfuscated way of doing it. There are elements which are not required here. Mainly that the new keyword is not needed (Function implies a function object). Function also comes with methods, such as call.
Function("this.a = 'hello'; console.log(this);").call({});
This will also work. Moreover, if you would like to retain what is in the variable a, you could always do this (demo):
var obj = {};
Function("this.a = 'hello'").call(obj);
obj.a;//hello
As for why it works with an empty context. You have actually given it a freshly constructed object with {}. So that is the scope that is used inside of the function when the reference this is encountered. In fact, call() does not need arguments if you are not intending to use scope. Something like this:
Function("console.log(5);").call();
will log 5 to the console without error.
The approach used in your question is to build a function from a magic string. That approach is pretty cool I must say, very similar to eval. But what you are looking for boils down to this difference I think:
function ball(){
var color = "red";
return color;
}
function Ball(){
this.color = "red";
}
console.log(ball());//logs red
console.log(new Ball().color);//logs red

Javascript create variable from its name

In PHP we can do this:
$variable = "name_of_variable";
$this->{$variable} = "somevalue";
how to do this in javascript?
where use case should look like:
function Apple(){
var name = "variable_name";
this.(name) = "value";
}
console.log(new Apple());
to output
[Apple: {variable_name:"value"}]
try:
this[name] = "value";
All objects can use dot and array notation for variable access.
Also note, this will allow you to create name value pairs that are inaccessible via dot notation:
var foo = {};
foo['bar-baz'] = 'fizzbuzz';
alert(foo.bar-baz); //this will not work because `-` is subtraction
alert(foo['bar-baz']); //this will work fine
If you are creating a new object literal, you can use string literals for the names for values with special characters:
var foo = {'bar-baz':'fizzbuzz'};
But you will not be able to use variables as the key within an object literal because they are interpreted as the name to use:
var foo = 'fizz';
var bar = { foo:'buzz' }
alert( bar.fizz ); //this will not work because `foo` was the key provided
alert( bar.foo ); //alerts 'buzz'
Because other answerers are mentioning eval, I will explain a case where eval could be useful.
Warning! Code using eval is evil, proceed with caution.
If you need to use a variable with a dynamic name, and that variable does not exist on another object.
It's important to know that calling var foo in the global context attaches the new variable to the global object (typically window). In a closure, however, the variable created by var foo exists only within the context of the closure, and is not attached to any particular object.
If you need a dynamic variable name within a closure it is better to use a container object:
var container = {};
container[foo] = 'bar';
So with that all being said, if a dynamic variable name is required and a container object is not able to be used, eval can be used to create/access/modify a dynamic variable name.
var evalString = ['var', variableName, '=', String.quote(variableValue), ';'].join(' ')
eval( evalString );
You can use square bracket notation in Javascript:
variable = "name_of_variable";
window[variable] = "somevalue";
You can do this with any object in Javascript.
var name = "var_name";
var obj = {};
obj[name] = 'value';
alert(obj.var_name);
I suggest using associative arrays to do whatever you're trying to do as they are significantly cleaner and easier to debug.
However if you really insist, you can use eval() to accomplish this:
variable = "name_of_variable";
eval(variable + " = \"somevalue\""); // this will work, but please do not do it
alert(name_of_variable);
EDIT: It his just come to my attention that a significantly easier (and better) way of doing this is by simply accessing the window object:
window[variable] = "somevalue";
http://jsfiddle.net/WJCrB/
window['name_of_variable'] = 'somevalue';
or
eval('var ' + variable_name + ' = ' + variable_name + ';');
Beyond that, don't do this. Variable variables are NEVER a good idea and make it nearly impossible to debug problems when (invariably) things break.

Categories

Resources