javascript - setting global variables inside a function - javascript

I am trying to create a function which will dynamically set the value of whatever global variable is passed as a parameter. It's not working, and I'm trying to figure out why. Can someone please explain why this doesn't work:
var things = 5;
function setup(variable) {
variable = 7;
}
setup(things);
console.log(things); //should return 7. returns 5 instead. the function had no effect on the global variable
and this also doesn't work:
var things = 5;
function setup(variable) {
window.variable = 7;
}
setup(things);
console.log(things); //should return 7, but returns 5. still not accessing the global variable.
but this does:
var things = 5;
function setup(variable) {
window[variable] = 7;
}
setup("things");
console.log(things); //returns 7
I suspect that what is happening is that the parameter variable is being set as a local variable inside of the function, so any changes are only happening to the local version. But this seems strange because the parameter that's been passed is a global variable. Can someone explain to me what is happening and how to better write this code? Does this require a method (which can then use this to access the original object)?
Thanks!!

Javascript is pass-by-value. (Objects, arrays, and other non-primitives are passed by value-of-reference.) That means that the value of the variable (or reference) is passed to the function, but the function parameter does not become an alias for the actual argument. Thus, you cannot change a variable outside a function without referencing it (as you do in your last example).
See this answer in another thread for more information.

Inside of functions are "variable environments". When the function setup is declared, and the parameter variable set, it creates a local variable in setup's variable environment for variable (the parameter).
So that is why this assignment
function setup(variable) {
variable = 7;
}
Will never change the value sent to variable.
Variables in JavaScript are values. As the variable is passed around, the only thing passed is the value of the variable. However, the value of the variable is assigned to the parameter (again poorly named in this example) variable. When the value of the parameter is assigned to 7, that only changes the local variable, and not the value of the passed variable.
//the value of things is 5
var things = 5;
//the passed value 5 is assigned to variable
function setup(variable) {
//the value of variable is changed to 7 (and nothing is done with 5)
variable = 7;
}
//the value of things is sent to setup
setup(things);
Hopefully this will be a little more enlightening. Consider a situation where setup was actually modifying the value of variable. A good example is when the value has state, such as an array or an object.
//the value of things this time is an object
var things = {};
//the passed value of object is assigned to variable
function setup(variable){
//the value of variable (the object) has a property added named msg with a value of "hello world"
variable.msg = "hello world";
}
//the value of things (an object) is sent to setup
setup(things);
alert(things.msg);//hello world

When variables are passed as arguments to functions, a copy of their value is made and assigned to the name of the argument in the function.
For example:
function foo(a) {
a = 7; // sets the temporary variable(argument) a to 7
}
var bar = 24;
foo(bar); // copies bar's value and passes in the copy to foo
For a function to modify a variable itself, you would have to access it another way. In other languages there are things called pointers that point to a place in memory. This allows you to modify variables directly, as you have where they are located - you can simulate this with JavaScript:
var spam = 3;
var memory = ["bar", 29, "x", foo, false];
function foo(a) {
memory[a] = 7;
}
foo(3);
The above example sets an array called memory and fills it with random gibberish. Then, a function named foo is created that allows for the modification of elements in this memory array.

Related

Passing an hash through parameter

I'm a little confused about the passing of a hash as parameter to a function.
I know that when a variable is passed to the function through a parameter, to this is applied a copy of the value.
But this is not valid with the hashes, at least not always.
Example:
var a = {b: 'hello world'};
var change = function(h){
h = true;
};
var change_v2 = function(h){
h.b = 'another hello';
};
console.log(a); // will print the original hash
change(a);
console.log(a); // naturally, will print the original hash
change_v2(a);
console.log(a); // this print b == 'another hello'
So, why the reference is used sometime for the hashes?
Can someone explain how javascript works in this case?
The simple answer to your question is that a value was copied -- a does not contain any object (what you called a hash) but actually contains a reference to the object and this reference was copied into the parameter local.
Read below for a more detailed explanation of why this happens, pass-by-value vs pass-by-reference.
In JavaScript, variables do not hold objects, they hold a reference to an object. This is an important distinction.
All parameters are pass-by-value in JavaScript. The object references stored in variables are passed by value.
These two behaviors combine to cause the specific behavior you are seeing. Let's lay out some examples of each passing behavior to prove that JavaScript passes arguments by value.
function foo(a) {
a.bar = 2;
}
var b = { bar: 1 };
foo(b);
b.bar would still be 1 if objects were passed by value, but this is not the case in JavaScript.
b.bar would be 2 if (a) the object reference was passed by value, or (b) the argument itself was passed by value.
So in this case, "pass reference by value" and "pass argument by reference" don't have a difference in behavior. Now let's introduce a case where they are different.
function foo(a) {
a = { bar: 2 };
}
var b = { bar: 1 };
foo(b);
b.bar will be 1 if the object is passed by value, which isn't what happens.
b.bar will also be 1 if the object reference is passed by value, which is what happens.
b.bar will be 2 if the argument is passed by reference.
So in summary, you have to realize two things:
Variables never contain objects, but they can contain a reference to an object. Two variables both holding a reference to the same object will reflect changes made to that object.
All arguments are passed by value, that is, the callee has no access directly to the variable that was passed in, but only to the value received by the parameter -- which can be an object reference. Manipulation of that object can cause the appearance of pass-by-reference, but note that the variable passed into the function still refers to the same object so the passed variable was not modified in any way.
As a side note, if objects were values that could be directly stored in variables, then {} === {} would be true, but it's not -- because the references are compared and found to be not equal, as these references refer to two different objects.
Javascript is pass-by-reference. Always. Assignment changes a name to refer to some value.
So,
function foo(h)
{ h = "bar"; }
The name h is a parameter to the function. The assignment affects the name h only, and that name exists only in the scope of the call to the function.
In contrast, the statement h.b = "bar"; does not assign to the name h. It assigns to the field b within the object that is referenced by the name h.
This is because JavaScript passes parameters by value.
Passing a parameter by value is like assigning formal parameters values to actual parameters. For example consider
function f(obj) { obj.a = 10; }
o = { a : 20 };
f(o);
The above code will function as if the value of o is assigned to obj like if you do
o = { a : 20 }
obj = o;
obj.a = 10;
console.log(o.a);
you will see the same effect.
See a detailed explanation here

Javascript: How can I create a function that 1. accepts a global variable as a parameter 2. alters the value of that global variable?

I'm trying to create a function that accepts a global variable as a parameter and changes the value of that global variable. Here's a simple example that illustrates what I'm trying to do:
var number = 20;
function setnew(num) {
num += 1;
};
setnew(number);
console.log(number) //-> 20
//I want this to return 21
Even after the setnew function is invoked, number remains unchanged. Can someone explain why number remains unchanged and suggest another way to achieve my intended goal?
Since Javascript does not support true reference passing where you can change the original variable, you can't directly do what you're asking. The usual work-around is to wrap the global in an object.
var myGlobal = {
counter: 0;
};
function increment(obj, prop) {
++obj[prop];
}
increment(myGlobal, "counter");
increment(myGlobal, "counter");
console.log(myGlobal.counter); // 2
Or, if the property name is already known, you can just do this:
var myGlobal = {
counter: 0;
};
function incrementCnt(obj) {
++obj.counter;
}
incrementCnt(myGlobal);
incrementCnt(myGlobal);
console.log(myGlobal.counter); // 2
This works because when you pass an object to a function, what is passed is a pointer to that same object (the object is not copied). So, if you modify any property on that object, there is only one copy of the object so you are modifying the property on that one copy of the object and everyone who has a reference to that object will see the property change.
Primitives like numbers and booleans are passed by value which works like a copy was made so the original can never be affected from the function.
Can someone explain why number remains unchanged and suggest another
way to achieve my intended goal?
When passing a number to a function, the value of the number is copied to a new variable which is the argument to the function. Changing the value of that argument to the function ONLY affects that argument - it has no connection at all to the original variable. This is because in Javascript primitives are passed by value, not by reference. So, no reference is maintained at all to where the value came from. When you receive it in the function, it is a new variable that stands on its own.
Another possible work-around is to just return the modified value from your function and allow the caller to assign that modified value to whatever they want:
var counter = 0;
function increment(val) {
return val + 10;
}
counter = increment(counter);
console.log(counter); // 10
If you were to pass in a object instead of a primitive, you would be able to update it.
For example:
var number = { value: 20 };
function setnew(num) {
num.value += 1;
};
setnew(number);
console.log(number.value) //-> 21
Basically when you pass in an object, javascript is actually only passing a reference to the variable, while when you pass an integer, it is going to pass a copy of the value.

When I make a reference to a global variable, why does the closure not reflect a change to the original?

In an app I'm developing, I define a global App namespace, where I store some properties that I want to access from different functions. For example I might keep a menu menuOpen property stored in my global App namespace so the function that I use to handle the Menu interface functionality can share this information easily with a different function that handles something else.
I was having a problem with this a moment ago, after updating this global variable with one function, and then checking a reference to it in the closure of another function, to find the reference didn't reflect my change.
I re-recreated this in a simple example, where I would expect false to be the result, since bar changes open to false, which should be reflected by state within foo's closure before the timeout completes and runs the check against state:
//set a global variable to be accessed by different parts
//of an application
var open = true;
//create a closure, which waits for a future event,
//then checks the "open" variable when it occurs
function foo() {
//reference to the global variable "open"
var state = open;
//set a timeout, to reflect "a future event", such
//as an event handler
setTimeout(function() {
if (state) {
$('html').text('true');
} else {
$('html').text('false');
}
}, 1000);
}
//change the "open" global within another function
function bar() {
open = false;
}
//create the closure
foo();
//change "open" to false, before the "if(state)" logic
//is called in the closure
bar();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
It was my understanding that a closure keeps a reference to a variable, so state would reflect any change to open. How does this actually work? And what would I need to do differently to make foo's closure aware of open's change?
This has nothing to do with closures. var x = "a"; var y = x; x = "b"; console.log(y) outputs "a", not "b", because y is a copy of x.
Closures don't change the normal behavior of variables. A closure is simply a function that uses local variables from a surrounding scope. foo is a closure because it uses open; the function passed to setTimeout is a closure because it uses state (but state never changes after being set).
You can fix your code by checking if (open) directly.
As others have said, this issue doesn't have anything to do with closures. It has to do with how Javascript does assignment of different types.
Only objects in Javascript are assigned by pointer. All other data types are assigned by value (e.g. the value is copied). So, when you do:
var state = open;
And, open is anything other than an object (which includes objects, arrays and functions), then the value in open is copied to state and the two variables then have nothing to do with one another.
See this example:
var myNum = 12;
var otherNum = myNum;
document.write("myNum = ", myNum, "<br>"); // 99
document.write("otherNum = ", otherNum, "<br>"); // 12
myNum = 99;
document.write("myNum = ", myNum, "<br>"); // 99
document.write("otherNum = ", otherNum, "<br>"); // still 12
But, if you assigned an object, it would just be a pointer to the original object so if the original was changed, you would see that change in the other reference to that same object:
function log(name, obj) {
document.write(JSON.stringify(obj) + "<br>");
}
var myObj = {greeting: "hello"};
var otherObj = myObj;
log("myObj", myObj);
log("otherObj", otherObj);
myObj.greeting = "bye";
log("myObj", myObj);
log("otherObj", otherObj);
Objects are assigned by pointer to the original. All other types of variables behave as if they are copied and no longer have a connection to the original.
If you want to take a normal non-object variable and get it to behave like you have a pointer to it, then you can put that variable into an object as a property on that object and you can pass the object around. Then, everyone who you pass the object to can examine the property and they will all be looking at exactly the same property value. If one changes it, they will all see the changed value.

Clarification of JavaScript variable scope

I already know how to make this code work, but my question is more about why does it work like this, as well as am I doing stuff right.
The simplest example I can make to showcase my issue is this :
Lets say I have a function that increments the value of an input field by 10 on the press of a button.
var scopeTest = {
parseValue : function( element, value ) {
value = parseInt( element.val(), 10 );
//Why does this not return the value?
return value;
},
incrementValue : function( element, button, value ) {
button.on('mousedown', function (e) {
//Execute the parseValue function and get the value
scopeTest.parseValue( element, value );
//Use the parsed value
element.val( value + 10 );
e.preventDefault();
});
},
init : function () {
var element = $('#test-input'),
button = $('#test-button'),
value = '';
this.incrementValue( element, button, value );
}
};
scopeTest.init();
The above code doesnt work because the parseValue method doesn't properly return the value var when executed inside the incrementValue method.
To solve it apparently I have to set the scopeTest.parseValue( element, value ); parameter to the value variable like this:
value = scopeTest.parseValue( element, value );
Than the code works.
But my question is why? Why is this extra variable assignment step necessary, why the return statement is not enough? Also I am doing everything right with my functions/methods, or is this just the way JavaScript works?
Working example here => http://jsfiddle.net/Husar/zfh9Q/11/
Because the value parameter to parseValue is just a reference. Yes, you can change the object, because you have a reference, but if you assign to the reference it now points at a different object.
The original version is unchanged. Yes, the return was "enough", but you saved the new object in a variable with a lifetime that ended at the next line of code.
People say that JavaScript passes objects by reference, but taking this too literally can be confusing. All object handles in JavaScript are references. This reference is not itself passed by reference, that is, you don't get a double-indirect pointer. So, you can change the object itself through a formal parameter but you cannot change the call site's reference itself.
This is mostly a scope issue. The pass-by-* issue is strange to discuss because the sender variable and the called functions variable have the same name. I'll try anyway.
A variable has a scope in which it is visible. You can see it as a place to store something in. This scope is defined by the location of your function. Meaning where it is in your source code (in the global scope or inside a function scope). It is defined when you write the source code not how you call functions afterwards.
Scopes can nest. In your example there are four scopes. The global scope and each function has a scope. The scopes of your functions all have the global scope as a parent scope. Parent scope means that whenever you try to access a name/variable it is searched first in the function scope and if it isn't found the search proceeds to the parent scope until the name/variable is found or the global scope has been reached (in that case you get an error that it can't be found).
It is allowed to define the same name multiple times. I think that is the source of your confusion. The name "value" for your eyes is always the same but it exists three times in your script. Each function has defined it: parseValue and incrementValue as parameter and init as local var. This effect is called shadowing. It means that all variables with name 'value' are always there but if you lookup the name one is found earlier thus making the other invisible/shadowed.
In this case "value" is treated similar in all three functions because the scope of a local var and a parameter is the same. It means that as soon as you enter one of the methods you enter the function scope. By entering the scope the name "value" is added to the scope chain and will be found first while executing the function. And the opposite is true. If the function scope is left the "value" is removed from the scope chain and gets invisible and discarded.
It is very confusing here because you call a function that takes a parameter "value" with something that has the name "value" and still they mean different things. Being different there is a need to pass the value from one "value" to the other. What happens is that the value of the outer "value" is copied to the inner "value". That what is meant with pass-by-value. The value being copied can be a reference to an object which is what most people make believe it is pass-by-reference. I'm sorry if that sounds confusing but there is too much value naming in here.
The value is copied from the outer function to the called function and lives therefor only inside the called function. If the function ends every change you did to it will be discarded. The only possibility is the return your "side effect". It means your work will be copied back to a variable shortly before the function gets discarded
To other alternative is indeed leaving of parameters and work with the scope chain, e.g. the global scope. But I strongly advize you not to do that. It seems to be easy to use but it produces a lot of subtle errors which will make your life much harder. The best thing to do is to make sure variables have the most narrow scope (where they are used) and pass the values per function parameters and return values.
This isn't a scoping issue, it's a confusion between pass-by-reference and pass-by-value.
In JavaScript, all numbers are passed by value, meaning this:
var value = 10;
scopeTest.parseValue( element, value );
// value still == 10
Objects, and arrays are passed by reference, meaning:
function foo( obj ){
obj.val = 20;
}
var value = { val: 10 }
foo( value );
// value.val == 20;
As others have said it's a pass-by-ref vs pass-by-val.
Given: function foo (){return 3+10;} foo();
What happens? The operation is performed, but you're not setting that value anywhere.
Whereas: result = foo();
The operation performs but you've stored that value for future use.
It is slightly a matter of scope
var param = 0;
function foo( param ) {
param = 1;
}
foo(param);
console.log(param); // still retains value of 0
Why?
There is a param that is global, but inside the function the name of the argument is called param, so the function isn't going to use the global. Instead param only applies the local instance (this.param). Which, is completely different if you did this:
var param = 0;
function foo() { // notice no variable
param = 1; // references global
}
foo(param);
console.log(param); // new value of 1
Here there is no local variable called param, so it uses the global.
You may have a look at it.
http://snook.ca/archives/javascript/javascript_pass

Do, inner and outer functions get their own copy of same variable?

just a bit confused by this code
var counter = function() {
//...
var count = 0;
return function () {
return count = count + 1;
}
}
var nextValue = counter();
console.log(nextValue());
nextValue.count = 7;
console.log(nextValue());
console.log(nextValue.count);
console.log(nextValue());
Output is
1
2
7
3
It's counter intuitive. There are two representations of count. One on the outerfunction nextValue and one that only the inner anonymous function can see.
Correct, or are my missing something?
The expression nextValue.count does not refer to the local variable "count" declared inside the function. It is not possible, in fact, to create a reference to a variable local to a function from code outside the function. What you're referencing there is simply a property of the function object.
So, yes, the "count" variable that the returned function has access to is effectively completely private to that function, and it is persisted in the closure formed by the call to the "counter" function.
If you did want that to work, you could do this:
function counter() {
function actual() {
return actual.count = actual.count + 1;
}
actual.count = 0;
return actual;
}
edit — (fixed bogus code) the name "actual" inside gives the returned function safe access to the function object itself; originally I typed "this" there, and that would not work unless the external code set it up explicitly.
The way you describe it, count is effectively a private variable. When you're assign to nextValue.count, you're creating a separate property--not accessing the internal count being incremented by your counter.

Categories

Resources