I have a global variable:
var chart;
A function which creates the chart:
function setChart(variableName, chartContainer){
variableName = new CanvasJS.Chart(chartContainer, {**params**});
variableName.render();
};
I call setChart
setChart(chart, "chartContainDiv");
If I call chart.render(); later, it doesn't work.
How can I achieve this? / What am I misunderstanding?
Since you're passing a string into the function, you end up trying to assign this:
setVar("globalVar");
// In setVar...
"globalVar" = 5
which obviously doesn't work. Passing in just the variable name itself will almost work as expected:
setVar(globalVar);
// In setVar...
globalVar = 5
HOWEVER
Because of variable scope, inside the setVar function you have a local variable with the same name as the global one. Doing any assignation here will just set the local variable to 5, where the global variable will remain at whatever value it used to be.
var myVar = 1;
function setVar(globalVar) { globalVar = 5; alert(globalVar); }
setVar(myVar); // alerts 5
alert(myVar); // alerts 1
Interestingly, if you pass the string in then you're able to set it via array-access on the window object:
setVar("globalVar");
// In setVar...
window[variableName] = 5; // window["globalVar"] = 5;
but trying to do that by passing the variable itself in doesn't work...
setVar(globalVar);
// In setVar...
window[globalVar] = 5; // window["5"] = 5 // or whatever globalVar contains
The TLDR version of this is that this is the only way to do this exactly as you're trying to do in the OP (although there are other ways such as Ahmad's answer, where you set a specific variable without passing it):
var myVar = 1;
function setVar(varName) { window[varName] = 5; }
setVar('myVar');
Use the function as a factory that returns a chart object, instead of passing it an empty variable. This uses the exact same concept, by creating an object and then returning that object, but it doesn't use a CanvasJS object for simplicity purposes:
function setChart(chartContainer) {
var variableName = new String(chartContainer);
return variableName.toUpperCase();
};
var chart = setChart("chartContainDiv");
var chart2 = setChart("blahBlah");
console.log(chart.toString()); // "CHARTCONTAINDIV"
console.log(chart2.toString()); // "BLAHBLAH"
http://jsfiddle.net/n8Lg4wqy/3/
first you have to be clear on the scopes of variables and global variables. In you example, there is no line where you set globalVar to 5. You only set a local variable for the function setVar called variableName to 5.
what you should do is:
var globalVar;
function setVar(){
globalVar = 5;
};
now if you want to have a global variable or a set of global variables then you should have them in an object and then have a function that take that variable name and an optional value that you to assign.
var globalVariables = {"globalvar1" : "", "globalvar2" : "", .... };
function setGlobalVar(variableName, Value) {
globalVariables[variableName] = value;
}
setGlobalVar ('globalvar1', 5); // this would do it
Related
I have the following closure:
var Container = (function () {
var variable;
var changeVariable = function () {
variable = 5;
};
return {
variable: variable,
changeVariable: changeVariable
};
})();
Container.changeVariable();
console.log(Container.variable);
The result is undefined, unless I set variable as:
Container.variable = 5
Why is that so? What's the difference? How should I do this properly?
Why is that so?
JavaScript assigns by value.
variable = 5; assigns the value 5 to the variable variable.
variable: variable, assigns the value of variable (at the time the code runs) to the property variable. It does not create a reference to the variable called variable.
When you later change the value of the variable called variable, you don't change the value of the property called variable.
How should I do this properly?
Create an object. Store the object locally. Manipulate that object. Return that object.
Forget about having the variable called variable entirely.
var container = (function() {
var self = {
variable: undefined,
changeVariable: changeVariable
};
function changeVariable() {
self.variable = 5;
}
return self;
})();
container.changeVariable();
console.log(container.variable);
(Aside: Convention reserves identifiers beginning with capital letters for constructor functions. I've renamed Container to follow that convention).
Use a getter:
return {
get variable() { return variable; },
changeVariable: changeVariable
};
Before I dive into the question I want to clarify that my use case involves patching a trans-compiler to generate a proper equivalent, hence the somewhat awkward question.
I want to shadow an outside variable but initialize it to the same value as outside as well. Here is an example:
var a = 2;
(function(){
var a = a;
a += 3;
// I want `a` to be 5
})();
// I want `a` to be 2
I realize with the above example the internal a will be NaN (undefined + 3), but can I initialize the variable doing the shadowing to the same one that it shadows somehow? Passing it as an argument is not an option as that function will be written by the user, the only thing that will be consistent is the presence of inner scope. I was thinking of changing the name of internal variable a but the compiler isn't currently built in a way to track it easily and this would introduce additional headaches.
You need to pass a as parameter in your IIFE.
(function(parameter){
// «parameter» contains the given value.
// parameter = "Some value".
})("Some value");
Something like this:
var a = 2; // Variable declaration in the global scope.
(function(a) {
a += 3;
// I want `a` to be 5
console.log(a); // Prints the current value in the local scope.
})(a); // The parameter: var a = 2;
console.info(a); // Prints the current value in the global scope.
// I want `a` to be 2
Since that is a immediately invoked function expression it has a completely different scope than the code written outside of it. There's no way to do what you are asking without passing in an argument in some way (whether directly when executing or using bind), or changing the function so the scope is that of the scope where the wanted var a is defined.
With that being said perhaps you can return some methods that will set a to the appropriate value.
http://jsbin.com/vazequhigo/edit?js,console
var a = 2;
w = (function(){
var setA = function(val) {
a = val;
}
var addA = function(val) {
a += val;
return a;
}
var a = 0;
return {
setA: setA,
addA: addA,
};
})();
w.setA(a);
console.log(w.addA(3));
I am currently learning javascript and came across this example
var t = function()
{
this.name = "Jam";
no = "123";
}
console.log(t.no); //Undefined
var m = new t();
console.log(m.name);
Why is the first statement undefined ?
t is a function object. As any other object, the function may have properties assigned. So in order your code to work you shall assign "123" to no property of your function (line A):
var t = function()
{
this.name = "Jam";
}
t.no = "123"; // line A
console.log(t.no); // "123"
var m = new t();
console.log(m.name);
Why is the first statement undefined ?
Because t doesn't have a property no.
First of all, the code inside the function, namely
this.name = "Jam";
no = "123";
is only executed when the function is called. You are doing this with var m = new t();, which comes after console.log(t.no);.
Secondly, no = "123"; does not create a property on the function object. It will attempt to set the value of variable no. Since the variable doesn't exist in your example, that line will either create a global variable no, or throw in error if your code is in strict mode.
Consider the following example:
var no = 21;
function foo() {
no = 42;
}
console.log(no); // 21
foo();
console.log(no); // 42
Because t is a function, that would be executed by t();. no on the other hand is a global scooed variable that is reached without prefix from everywhere.
t is a function expression. You you can access a returned object of a function like t().no or you can create a new object by using the function as a constructor like this
myT = new t()
console.log(t.no);
But your no variable is just a global variable inside the function and it is not a part of what it returns nor it is not attached to the returning object of the constructor function.
Here is a very good tutorial which covers all these topics at depths.
var value = 10;
var outer_funct = function(){
var value = 20;
var inner_funct = function(){
var value = 30;
console.log(value); // logs 30
console.log(window["outer_funct"]["value"]); // What I would like to log here is the value 20.
console.log(window["value"]); // logs 10
};
inner_funct();
};
outer_funct();
I believe the reason the second log is returning undefined is because window["outer_funct"] refers to the function object, and the function object doesn't have a property "value" associated with it. Instead, what I would like to do is refer to the execution context when window["outer_funct"] is invoked. Is this possible to do within the execution context of inner_funct?
I believe the reason the second log is returning undefined is because window["outer_funct"] refers to the function object, and the function object doesn't have a property "value" associated with it.
Correct.
Instead, what I would like to do is refer to the execution context when window["outer_funct"] is invoked. Is this possible to do within the execution context of inner_funct?
No, not with you having shadowed value (declared it in inner_funct). You have no way of getting to it with that symbol having been overridden like that. You could, of course, grab it into another symbol:
var value = 10;
var outer_funct = function(){
var value = 20;
var outer_value = value;
var inner_funct = function(){
var value = 30;
console.log(value); // logs 30
console.log(outer_value); // logs 20
console.log(window.value); // logs 10
};
inner_funct();
};
outer_funct();
If you hadn't shadowed it, then you could refer to value in the containing context, e.g.:
var value1 = 10;
var outer_funct = function(){
var value2 = 20;
var inner_funct = function(){
var value3 = 30;
console.log(value3); // logs 30
console.log(value2); // logs 20
console.log(value1); // logs 10
};
inner_funct();
};
outer_funct();
It's worth noting that the only reason that your original code's window["value"] returned 10 (btw, you could also use window.value) is that the var value = 10; is at global scope. All variables declared with var become properties of the global object, which on browsers is referred to via window (technically, window is, itself, just a property on the global object that points back to the global object).
You cannot refer to value using window["outer_funct"] exactly for the reasons you mentioned. What you can do is something like this:
var value = 10;
var outer_funct = function(){
var context = {// you can put multiple values in here
value: 20;
}
var inner_funct = function(){
var value = 30;
console.log(value); // logs 30
console.log(context.value); //logs 20
console.log(window["value"]); // logs 10
};
inner_funct();
};
outer_funct();
Another way you can do it is if you haven't shadowed value inside inner_funct. If you don't have a variable named the same thing, you can log it and it will return 20. But since you created another variable named value inside inner_funct, that will shadow the value of value in outer_funct.
I would also question why you would need to have three variables named exactly the same, across three scopes.
Local variables are intended to be non-accessible, also because they can depend on the function execution (how could you access that variable if the function has never been executed?).
If you really need some trick, you can have a look at this:
var person = function () {
// Private
var name = "Robert";
return {
getName : function () {
return name;
},
setName : function (newName) {
name = newName;
}
};
}();
alert(person.name); // Undefined
alert(person.getName()); // "Robert"
person.setName("Robert Nyman");
alert(person.getName()); // "Robert Nyman"
and notice that the function must be executed before you can use accessible methods.
No, it is absolutely impossible to access non-global shadowed variables in JavaScript.
You cannot get the execution context of a function as well, it is an implementation-dependent internal value (specification type) - you are correct, your code was looking for properties on the function object.
Variables in the global scope could be accessed as properties of the global object (window in browser), but if you are shadowing a local variable your only choice is to rename your own variable that casts the shadow.
var value = 30; is a local variable in function outer_funct, it could not be accessed from outside of this function.
in your code, although winodw["outer_funct"]["value"] is written inside inner_funct but it acts as trying to access a local variable from out of outer_funct because by `window['outer_funct'] you are staying at the top level.
Variables don't become properties of the functions under which you define them. Excluding the window object (in the case where they are globally declared) there's no object off of which you can access a locally-defined variable. There are workarounds as suggested by the other answers, but they are still a testament to JavaScript's inability to perform such a task in the actual circumstance that you've shown us.
I have a global variable MyGlobalVar and some code that looks like this:
var MyGlobalVar = null;
function PlayWithMyGlobal() {
MyGlobalVar = new Object();
.... adding properties to MyGlobalVar
MoreFun(MyGlobal);
}
function MoreFun(TheVar) {
is TheVar here a local or just a reference to the global?
}
If I pass the global variable, am I still working with the global?
Thanks.
If I pass the global variable, am I still working with the global?
Thanks.
It depends whether variable you pass is an object or a primitive (number, string, boolean, undefined, null are primitives) value in the first place. Objects are passed by reference and primitives by value.
In your case, you are passing object:
MyGlobalVar = new Object();
And in JS, objects are passed by reference. So you are still working on that variable.
You can confirm it like this:
var MyGlobalVar = null;
function PlayWithMyGlobal() {
MyGlobalVar = new Object();
MoreFun(MyGlobalVar);
}
function MoreFun(TheVar) {
MyGlobalVar.foo = 'I am foo'; // property created here
}
PlayWithMyGlobal();
console.log(MyGlobalVar.foo); // I am foo
If the global variable is an object, then you're still working with the global variable. Otherwise, it's a copy.
As shown and annotated below, your variables point to the same global object.
var MyGlobalVar = null;
function PlayWithMyGlobal() {
MyGlobalVar = new Object(); // <--- Object
MoreFun(MyGlobalVar); // <--- Passing object reference
}
function MoreFun(TheVar) {
TheVar.test = 'Did I modify global?';
alert(TheVar === MyGlobalVar); // true
alert(MyGlobalVar.test); // "Did I modify global?"
}
Yes, you have a local reference to the same object that is referenced globally.
A simple test would be...
console.log(MyGlobalVar === TheVar); // should be true