Accessing variables from IIFE - javascript

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

Related

When IIFE return a value, where does the value exist?

When I tried the example on babel Symbol in the example there was no return, so I added it cause I thought it should be(I am not sure if I am right).
It logged in my console MyClass is not defined.
If IIFE returns a Class, why it said MyClass is not defined?
(function() {
var key = Symbol("key");
function MyClass(privateData) {
this[key] = privateData;
}
MyClass.prototype = {
doStuff: function() {
}
};
return MyClass //this is no return for the original example
})();
var c = new MyClass("hello")
c["key"]
As with any other function call, the value goes to the left hand side of the function.
var return_value_of_x = x();
or
var return_value_of_iife = (function () { return 1; })();
Since you have nothing on the LHS of your IIFE, the value is discarded.
In your example MyClass is a variable declared within the IIFE. It doesn't exist outside that function. You could create another variable with the same name in the wider scope:
var MyClass = (function () { …
You can store the value somewhat like this if your IIFE is returning a value.
let returnedValue = (function(){console.log('return'); return 2;})();

Passing string into function doesn't set variable name

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

Retrieving values from Execution Contexts in JavaScript

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.

how to declare a global variable assign value in one function and use in another function in javascript

I just write a test html file to learn about object in javascript. The code is as follows
in script tag
<script type="text/javascript">
var obj = new ParentFn();
var obj2 = new AnotherParentFn();
var temp;
function initer()
{
temp = obj.Adding();
obj2.caller();
}
function ParentFn()
{
this.a = 10;
this.b = 20;
}
function AnotherParentFn()
{
this.a = 30;
this.b = 50;
}
AnotherParentFn.prototype.caller = function()
{
var self = this;
temp();
}
ParentFn.prototype.Adding = function()
{
var self = this;
document.getElementById("id_div1").innerHTML = " Method Called and Result of a+b is " + (self.a + self.b);
}
</script>
In body i use
<button onclick="initer()"> Click here to test </button>
<div id="id_div1"></div>
Problem is when AnotherParentFn.prototype.caller is called from initer() function temp variable is still undefined. What is wrong with the code??
My task is to assign the function ParentFn.prototype.Adding in a global variable and call the global variable from AnotherParentFn.prototype.caller function. How to achieve it?
You don't need to save it as a global variable. It's already saved in ParentFn.prototype. All you need to do is invoke it with .call and pass in your desired receiver. You can implement AnotherParentFn.prototype.caller like this:
AnotherParentFn.prototype.caller = function()
{
ParentFn.prototype.Adding.call(this);
}
This way you can get rid of temp completely. You also don't need to assign this to a local var self everywhere.
Parentheses are used to execute a function.
When you assign the value to temp, you are calling the function and assigning the result (undefined) to temp. To store a reference to the function in temp, omit the parentheses.
temp = obj.Adding;
By writing temp = obj.Adding(); it stores the return value. not function pointer in temp. Use this
function initer()
{
temp = obj.Adding;
obj2.caller();
}
First of all, the reference to obj.Adding is not assigned properly; it should be this (without parentheses):
function initer()
{
temp = obj.Adding;
obj2.caller();
}
Then, inside AnotherParentFn.prototype.caller itself, you must pass the current object as this explicitly during the invocation by using .call():
AnotherParentFn.prototype.caller = function()
{
temp.call(this);
}

Is this a javascript closure?

I have this....
function MyFunction() {
var myVar = "I think I am encapsulated";
this.getMyVar = function() {
return myVar;
}
}
var myProperty = new MyFunction();
console.log(myProperty.getMyVar());
myProperty.myVar = "you're not encapsulated";
console.log(myProperty.getMyVar());
It outputs: "I think I am encapsulated twice". Why? I did not think this was a closure...
The closure is around the "getMyVar" function. The variable "myVar" inside the constructor is a local variable, and not visible outside the function except as the return value from "getMyVar".
Setting a "myVar" property on the object does just that, but the "getMyVar" function is not returning a property of an object; it's returning the value of the local variable in the closure.
Yes, it is.
When you define a function inside of another function, the inner function has access to all of the outer function's local variables...
In your case, getMyVar has access to myVar - through the closure.
var myVar = "I think I am encapsulated";
this.getMyVar = function() {
return myVar;
}
This is a closure, and the myVar variable from the time the function was created will be returned. Notice that's it a local variable, so there's no other way to access it after this function exits.
var myVar = "I think I am encapsulated";
Notice that this is not this.myVar (the variable you're setting later with myProperty.myVar).
Probably what you're trying to do is:
function MyFunction() {
this.myVar = "I think I am encapsulated";
this.getMyVar = function() {
return this.myVar;
}
}

Categories

Resources