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.
Related
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));
From the MDN description of Function:
Note: Functions created with the Function constructor do not create
closures to their creation contexts; they always are created in the
global scope. When running them, they will only be able to access
their own local variables and global ones, not the ones from the scope
in which the Function constructor was called. This is different from
using eval with code for a function expression.
I understand,
var y = 10;
var tester;
function test(){
var x = 5;
tester = new Function("a", "b", "alert(y);");
tester(5, 10);
}
test(); // alerts 10
Replacing the tester = new Function("a", "b", "alert(y);"); with tester = new Function("a", "b", "alert(x);");, I will get
// ReferenceError: x is not defined
But couldn't understand the author's line-
...they always are created in the global scope.
I mean how is the new Function("a", "b", "alert(y);"); nested within the test fn is in global scope?
In fact, accessing it from outside the test fn will simply result in
Uncought TypeError:tester is not a function
Please elucidate.
In your example, "created in the global scope" means that tester will not have closure over x from test:
function test(){
var x = 5;
tester = new Function("a", "b", "alert(x);"); // this will not show x
tester(5, 10);
}
When you new up a Function, it does not automatically capture the current scope like declaring one would. If you were to simply declare and return a function, it will have closure:
function test(){
var x = 5;
tester = function (a, b) {
alert(x); // this will show x
};
tester(5, 10);
}
This is the trade-off you make for having dynamically compiled functions. You can have closure if you write the function in ahead of time or you can have a dynamic body but lose closure over the surrounding scope(s).
This caveat doesn't usually matter, but consider the (slightly contrived) case where you build a function body as a string, then pass it to a function constructor to actually be evaluated:
function addOne(x) {
return compile("return " + x + " + 1");
}
function addTwo(x) {
return compile("return " + x + " + 2");
}
function compile(str) {
return new Function(str);
}
Because the function is instantiated by compile, any closure would grab str rather than x. Since compile does not close over any other function, things get a bit weird and the function returned by compile will always hold a closure-reference to str (which could be awful for garbage collection).
Instead, to simplify all of this, the spec just makes a blanket rule that new Function does not have any closure.
You have to create an object to expose via return inside the test() function for it to be global. In other words, add var pub = {} and name your internal functions as properties and/or methods of pub (for example pub.tester = new func) then just before closing test() say return pub. So, that way it will be publically available (as test.tester). It's Called the Revealing Module Pattern.
What it means is that inside the function you can only refer to global variables, as you've found. However, the reference to the function itself is still in the local scope where it was created.
I'm confused as to where the confusion is.
It says that the function will be in global scope...and therefore will only have access to its own scope and the global scope, not variables local to the scope in which it was created.
You tested it and it has access to its own scope and the global scope, not variables local to the scope in which it was created.
So where's the confusion?
Is it in your assigning of the function to the variable testing? testing is just a local variable with a reference to the function...that has nothing to do with the scope of the creation of the function.
Scope is lexical, and has to do with where the function is created, not what random variables a function reference happens to be assigned to at runtime. And the documentation is telling you that when you make a function this way it acts as if it was created in the global scope...so it's acting completely as expected.
Here's an illustration:
This:
var y = 10;
var tester;
function test()
{
var x = 5;
// 10 and errors as not defined
tester = new Function("console.log(y); console.log(x);");
}
Is similar to this:
var y = 10;
var tester;
function test()
{
var x = 5;
// also 10 and errors as not defined
tester = something;
}
function something()
{
console.log(y);
console.log(x);
}
NOT
var y = 10;
var tester;
function test()
{
var x = 5;
// 10 and 5...since x is local to the function creation
tester = function()
{
console.log(y);
console.log(x);
}
}
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
I'm trying to create a function which returns another function. I want separate information when each of the inner function is run, but this isn't happening. I know that explanation is not great, so I've put together a small example.
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return that;
})(testVal);
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());
This outputs 2, 2. What I would like is 2, 4 to be output. I know this isn't explained perfectly, so if it's not clear what I'm trying to achieve, can someone explain why the variable seems to be shared across the two functions?
Thanks
Like this ?
var testFn = function(testVal) {
var test = testVal
return {
getVal: function() {
return test
}
}
};
var ab = testFn (4)
var ac = testFn (2)
console.log(ab.getVal(),ac.getVal()) //4 //2
The problem in your code is this.getVal() / returning this
because 'this' refers to the global scope / Window
You are glubbering with the global namespace and overwriting Window.getVal() , the moment you are setting b = testFn (2)
This results in overwriting as method getVal too because they both refer to the global Object and always share the same method getVal
Therefore they share the same closure and are outputing 2
console.log("The same: " + (Window.a === Window.b)) // true
console.log("The same: " + (a === b)) // true
you can see that if you change it a little:
var testFn = function(testVal) {
var x = {}
return (function(testVal) {
var test = testVal;
x.getVal = function () {
return test;
}
return x
})(testVal);
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());//4 2
it suddenly works because it results in 2 different Objects returned (btw you don't even need the outer closure)
console.log("The same: " + (a === b)) // false
Here are the JSbins First / Second
I hope you understand this, I'm not good in explaining things
If theres anything left unclear, post a comment and I'll try to update the answer
This question comes down to the context in which functions are invoked in JavaScript.
A function that is invoked within another function is executed in the context of the global scope.
In your example, where you have this code:
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return this;
})(testVal);
}
The inner function is being called on the global scope, so this refers to the global object. In JavaScript a function executed within another function is done so with its scope set to the global scope, not the scope of the function it exists within. This tends to trip developers up a fair bit (or at least, it does me!).
For argument's sake, lets presume this is in a browser, so hence this refers to the window object. This is why you get 2 logged twice, because the second time this runs, this.getVal overwrites the getVal method that was defined when you ran var a = testFn(4);.
JavaScript scopes at function level, so every function has its own scope:
var x = 3;
function foo() {
var x = 2;
console.log(x);
};
console.log(x); //gives us 3
foo(); // logs 2
So what you want to do is run that inner function in the context of the testFn function, not in the global scope. You can run a function with a specific context using the call method. I also recorded a screencast on call and apply which discusses this in greater detail. The basic usage of call is:
function foo() {...}.call(this);
That executes foo in the context of this. So, the first step is to make sure your inner function is called in the right context, the context of the testFn method.
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return this;
}.call(this, testVal);
}
The first parameter to call is the context, and any arguments following that are passed to the function as parameters. So now the inner function is being called in the right scope, it wont add getVal to the global scope, which is a step in the right direction :)
Next though you also need to make sure that every time you call testFn, you do so in a new scope, so you're not overwriting this.getVal when you call testFn for the second time. You can do this using the new keyword. This SO post on the new keyword is well worth reading. When you do var foo = new testFn() you create and execute a new instance of testFN, hereby creating a new scope. This SO question is also relevant.
All you now need to do is change your declaration of a and b to:
var a = new testFn(4);
var b = new testFn(2);
And now console.log(b.getVal(), a.getVal()); will give 2, 4 as desired.
I put a working example on JSBin which should help clear things up. Note how this example defines this.x globally and within the function, and see which ones get logged. Have a play with this and hopefully it might be of use.
The output you get is (2,2) because when you do
var that = this;
what you actually get is the global object (window),
the object that holds all the global methods and variables in your javascript code.
(Note that every variable that is not nested under an object or function is global and
every function that is not nested under an object is global, meaning that functions that are nested under a function are still global)
so, when you set:
var test = testVal;
this.getVal = function() {
return test;
}
you actually set the function "getVal" in the global object, and in the next run you will again set the same function - overriding the first.
To achieve the affect you wanted I would suggest creating and object and returning it in the inner function (as #Glutamat suggested before me):
var testFn = function(testVal) {
return new Object({
getVal: function() {
return testVal;
}
});
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());
In this way, in the outer function we create an object with an inner function called "getVal" that returns the variable passed to the outer function (testVal).
Here's a JSBin if you want to play around with it
(thanks to #Glutamat for introducing this site, I never heard of it and it's really cool :D)
Using JavaScript, say I have a function X, and in that function an object called objectX is created. function X returns objectX. Later in the code function Z(somevar, anObject) receives objectX as one of it's parameters.
Now in function Z, is objectX and all its properties referred to as anObject inside function Z?
And what happens if function Z returns anObject? Will the rest of the code see the object as "objectX" or "anObject"?
function X() {
...
objectX = {};
...
return objectX;
}
X();
function Z(anything, anObject) {
...
return anObject
}
Z(something, objectX);
anObject and objectX both are referencing to the same space in memory, so, name it as you want, it's always the same object.
Good luck!
This is mostly a question of scope.
function X() {
// local objectX, only accessible through this name inside X()
var objectX = {};
objectX.foo = 'bar';
return objectX;
}
function Z(somevar, anObject) {
// anObject is passed in as a parameter
// it's only accessible through this name inside Z()
anObject.foo = somevar;
return anObject;
}
// get the 'objectX' from X() and store it in global variable a
var a = X();
// pass the received 'objectX' into Z()
// note that the variable names objectX and anObject cannot be accessed
// because they are local variables of the functions X() / Z()
var b = Z('baz', a);
// a is now the same as b, they both reference the same var
// a.foo and b.foo both are set to 'baz'
I believe an example is the best way to teach. Here is some code (click here to see it in JS Bin):
// Defines the variable to keep track of how many objects X() defines.
var num = 1;
// Instantiate another variable to see if it is changed by Z().
var anObject;
// Creates an object with a comment and a random number.
function X() {
// Create an object and give it a name.
var objectX = {comment : "Creation #" + num};
// Increase the value of num.
num++;
// Add another random number between 0 and 100 inclusively.
objectX.randNum = Math.round(Math.random() * 100);
// Return objectX.
return objectX;
}
// Modifies the second parameter by adding the value of the first parameter.
function Z(somevar, anObject) {
anObject.somevar = somevar;
return anObject;
}
var objectX = X(), objectY = X();
objectX2 = Z('coolness', objectX);
// Notice that objectX is still the result of calling X() the first time.
alert("objectX.comment = " + objectX.comment);
// Notice that objectX is not equal to objectY.
alert("objectX === objectY evaluates to " + (objectX === objectY));
// Notice that objectX2 is the same thing as objectX.
alert("objectX === objectX2 evaulates to " + (objectX === objectX2));
// Notice that anObject is not defined.
alert("typeof anObject evaluates to " + (typeof anObject) + " after Z is called.");
alert("Now review the JavaScript code.");
If read through the comments, you will find the answers to your questions. First you will notice that in function Z, since I passed objectX as the second parameter, inside of the function, it could be referred to by anObject. Second you will notice that once outside of function Z, anObject no longer refers to objectX. The comments also reveal other things that are true in JavaScript.
Javascript has function scope. This means that every variable declared within a function, will only be accessible from within that function.
If you’d properly declared the objectX variable with var, as follows:
function X() {
...
var objectX = {};
...
return objectX;
}
then objectX would only be known as objectX inside the X function. Elsewhere, it would be known as whatever variable you’d assigned it to. Since in your code, you don’t assign the result of X() to anything, objectX would not be accessible from anywhere.
However, here’s one of Javascript’s more serious design flaws: if you don’t explicitly declare a variable (using the var statement, or as a function parameter), that variable will automatically become a global variable. That means that it will be accessible anywhere.
Because of this, in your code above, you can access objectX everywhere by that name.
anObject, on the other hand, is properly declared (as a parameter), and that means its scope will be limited to the Z function.
In short, the way your code is written, objectX is accessible everywhere by way of the objectX variable, and inside the function Z, you can reference it both as objectX and as anObject.
Do note, however, that global variables are a Bad Thing™, since they can make it quite hard to figure out what variable gets assigned by who, when, and why — as you’ve noticed.
While Javascript makes it impossible to completely avoid them, as a rule you should try to keep the scope of your variables as small as possible (scope = where in your program that variable can be accessed).
To that end, I would recommend refactoring your code like igorw has.
Here is link to jsfiddle
Lets take the following example below:
Person = function(name){
this.name = name;
}
function x(){
var john = new Person('john');
return john;
}
function z(tempVar, anObject){
var newObj = anObject;
newObj.name = tempVar;
return newObj;
}
myPerson = x();
console.log(myPerson.name); //john
console.log(z('peter', myPerson).name); //peter
console.log(myPerson.name); //peter
You can see, even though you created a new object in z but because they are referencing to the same object myPerson's name property is also changed after z() is called.