Javascript closures - lifetime of variables - javascript

Being fairly new to Javascript and from a c# background I have been stumbling along adequately. I knew that soon enough I would need to get my head round the fact that functions are objects in their own right and that JS closures are often the cause of confusion.
I am trying to understand this little snippet of code
// Function which returns object with function properties
function myFunc() {
value = 42;
var result = {
value: value,
getValue: getValue,
incrementValue: incrementValue,
setValue: setValue,
};
return result;
function setValue(y) {
value = y;
};
function getValue() {
return value;
};
function incrementValue() {
value++;
};
};
// Helper function to print out results
function printResults(m,x){
$('#output').append(m + ': ' + x).append('<br/>');
};
var myObject = myFunc(); // returns the object
printResults('Inital call to getValue',myObject.getValue());
myObject.setValue(59);
printResults('Called changeValue',myObject.getValue());
printResults('Value property of object',myObject.value);
printResults('Called getValue again',myObject.getValue());
myObject.incrementValue();
printResults('Call increment value',myObject.getValue());
printResults('Value property of object',myObject.value);
I get the following results when run in jsFiddle
Inital call to getValue: 42
Called changeValue: 59
Value property of object: 42
Called getValue again: 59
Call increment value: 60
Value property of object: 42
These show that the functions are using the variable value within their closure and this persists between invocation of the inner functions. BUT, the value of value does not change in the returned object.
I think I get the basic point that functions are executed using the scope chain that was in effect when they were defined.
Questions
Can I make the value property of the returned object operate in the same way - or is the only way to return it via a function, since the latter retains the variable in its closure?
And, just for confirmation, for every invocation of myFunc(), I assume I will get an object whose function properties will have their own scope chain and therefore independent of each invocation.

First of all, do not forget the var keyword when declaring variables. When you declare value = 42 inside myFunc, you are actually creating a variable in the global namespace instead of the function scope. It should start like this:
function myFunc() {
var value = 42;
Now, myObject.result is returning 42 because myFunc returns your result object which contains a copy of the value variable declared inside the function.
Your functions setValue, getValue and incrementValue are changing the value of value, not result.value. When you call myObject.value, you are getting the value from the returned object, not the inner variable of your function.
You could get it to work using something like this:
function myFunc() {
var value = 42;
var result = {
value: value,
getValue: getValue,
incrementValue: incrementValue,
setValue: setValue
};
return result;
function setValue(y) {
result.value = y;
}
function getValue() {
return result.value;
}
function incrementValue() {
result.value++;
}
}
However, there are better design patterns than this. You could use the new keyword and prototype to define the methods available for the objects returned from your function. Take this example:
function myFunc() {
this.value = 42;
}
myFunc.prototype.setValue = function(y) {
this.value = y;
}
myFunc.prototype.getValue = function(y) {
return this.value;
}
myFunc.prototype.incrementValue = function(y) {
this.value++;
}
var myObject = new myFunc();
console.log(myObject.getValue()); // 42
myObject.setValue(30);
myObject.incrementValue();
console.log(myObject.getValue()); // 31

Yes, you can:
var result = {
get value() {
return value;
},
getValue: getValue,
incrementValue: incrementValue,
setValue: setValue,
};
Hooray for ECMAScript 5. Of course, this won’t work on IE < 8.
<aside>value = 42; should be var value = 42;.</aside>
This doesn’t have a lot to do with the lifetime of variables, by the way – it’s just how assignment works. There are references in JavaScript, but no “reference variables” or “reference properties”. The object contains a copy of whatever value was at the time; creating a getter like this is just like creating a function that’s called implicitly.

Can I make the value property of the returned object operate in the same way
If you mean that it shows the updated value, yes, you can do that. You just have to change the code to update the value property as well:
function myFunc() {
var value = 42; // don't forget var!
var result = {
value: value,
getValue: getValue,
incrementValue: incrementValue,
setValue: setValue,
};
return result;
function setValue(y) {
result.value = value = y;
}
function getValue() {
return value;
}
function incrementValue() {
value++;
result.value = value;
}
}
The reason why I choose to use both value and result.value is to prevent the modification of the value through result.value. If you notice, I don't internally read from result.value, I only write to it. That means that assignments to result.value from external code doesn't have an effect. This conforms to how your existing code works.
And, just for confirmation, for every invocation of myFunc(), I assume I will get an object whose function properties will have their own scope chain and therefore independent of each invocation.
Yes, every invocation of myFunc creates a new object and new functions and they are completely independent from objects/functions created by previous invocations.

Related

JavaScript to set value of variable inside it, and then after retrive variable's data on another call

I want a function in JS, which can take some value at first call and set it to a variable inside it.
And then again on another call i want to again get the data of that variable.
something like this:
while calling the function for first time
function set_and_get(value){
var a = value;
}
on another call it should return the value like
returned_value = set_and_get()
Thanks.
Normally the pattern looks more like this where the function will return another function which you then would use.
function set_and_get(value){
return function () {
return value;
}
}
var test = set_and_get(1)
console.log(test())
var test2 = set_and_get(2)
console.log(test(), test2())
Now if the function can not be reused, aka once it is set, it is done. You could just overwrite the function. It is not the best practice, but it can work.
function set_and_get(value) {
set_and_get = function() {
return value;
}
return value
}
console.log(set_and_get(1))
console.log(set_and_get())
console.log(set_and_get(2))
While not necessarily recommended, you can attach a property to the function object itself:
function get_and_set(value) {
if (value !== undefined) {
get_and_set._value = value;
} else {
return get_and_set._value;
}
}
but note that the property is in no way protected - it is possible to read it (And overwrite it) from outside the helper function.
You would have to use a combination of concepts called closure and high order function.
var set_and_get_fn = function(value){
var a = value;
var set_and_get = function(){
return a;
}
return set_and_get;
}
var set_and_get = set_and_get_fn(10);
var returned_value = set_and_get();
// 10
set_and_get();
// still 10
set_and_get = set_and_get_fn(12);
set_and_get();
//12
set_and_get();
//12
Closure allows an inner function set_and_get to have access to the variables of it's outer function/context set_and_get_fn, even after set_and_get_fn has returned. set_and_get has closed over variable a. High order functions allows you to treat functions as objects, you can pass them as arguments and return them from functions. I'm returning set_and_get function from set_and_get_fn

Passing a global variable to a function

How come the following code is giving me a 0 instead of a 1? I want my function to change a variable declared outside the function but I do not want to specify the variable in the function declaration.
that = 0;
function go(input) {
input++;
}
go(that);
console.log(that);
As answered by Oriol, it doesn't work because the variable is passed by value, so you're not changing the "that" variable. A workaround would be to pass the variable name :
that = 0;
function test(input) {
window[input]++;
}
test("that");
console.log(that); // 1
That's because you are passing the variable by value, not by reference.
In javascript, all variables are passed by value, except objects, which are passed by reference (well, in fact they are passed by value too but they are a reference, see below).
And you can't change that behaviour.
Edit: If you don't know what passing by value/reference means, you should read a tutorial. But here you have some examples:
Variable passed by value
function foo(bar){
console.log(bar); // 1
bar++;
console.log(bar); // 2
}
var mybar = 1;
console.log(mybar); // 1
foo(mybar);
console.log(mybar); // 1
Variable passed by (value but used as a) reference
function foo(bar){
console.log(bar.a); // 'b'
bar.a = 'c';
console.log(bar.a); // 'c'
}
var mybar = {a:'b'};
console.log(mybar.a); // 'b'
foo(mybar);
console.log(mybar.a); // 'c'
In your case
You can do
Make your variable a property of an object (in your case, since it's a global variable, use window) and pass the object (reference), so you can alter it
window.that = 0;
function go(obj) {
obj.that++;
}
go(window);
console.log(that); // 1
Use a return value
var that = 0;
function go(input) {
return input++;
}
that = go(that);
console.log(that); // 1
Note that you can't do
Convert your variable into an object
var that = new Number(0); // Now it's an object number
function go(input) {
input++;
}
go(that);
that *= 1; // Now it's a literal number
console.log(that); // 0
That's because objects are passed by value too, but they are a reference. That means that inside the function you can change the properties of the outer object (because it's a reference) but you can't change the entire object, because it's passed by value.
See examples here: https://stackoverflow.com/a/3638034/1529630
This has to do with pointers, scope, passing variables by reference, and all that jazz.
If you really want to do this, you can pass an object in Javascript like this:
var that = {value: 0};
function go(input) {
input.value++;
}
go(that);
console.log(that.value);
All we've done is made that an object which is by definition passed as a reference in Javascript. Then we just make sure we properly modify the object's attributes.
Your code
that = 0; //Global variable
function go(input) { //input is argument and is not passed by reference
input++; //This just increments a local copy i.e 0
}
go(that); //Passed 0
console.log(that);
Instead do this
that = 0;
function go() {
that++;
}
go(); //Not passing any variable .. function can already see the gloabl "that"
console.log(that); // This will print gloabl i.e. 1
Actually you could just add console.log(input) inside the function and it would work just fine.
Please correct me if i'm wrong. Hope i helped !!
I would be glad if somebody could explain why im wrong

Very confused about JavaScript scopes

Below is a greatly simplified snippet of code derived from a SlickGrid sample:
model.js:
(function($) {
function RemoteModel() {
var someArr=[0];
var someVar=0;
var onDataLoadedSuccess = new Slick.Event();
function ensureData(from, to) {
h_request = setTimeout(function() {
req = $.ajax({
url: url,
context: this,
success: function(resp) {
onSuccess();
}
});
}, 3000);
}
function onSuccess() {
someVar=someVar+100;
someArr[0]=someArr[0]+100;
console.log(someVar + " " + someArr[0]); // yes values are changing properly for both !
onDataLoadedSuccess.notify({ from: from, to: to });
}
return {
"someArr": someArr,
"someVar": someVar,
"onDataLoadedSuccess": onDataLoadedSuccess,
};
}
})(jQuery);
main.js:
var loader=new RemoteModel();
var grid=new Slick.Grid("#wlGrid", loader.data, columns, options);
grid.onViewportChanged.subscribe(function(e, args) {
var vp = grid.getViewport();
loader.ensureData(vp.top, vp.bottom);
});
loader.onDataLoadedSuccess.subscribe(function(e, args) {
// someVar is always 0 :( ! someArr[0] is changing fine .. why ???
console.log(loader.someVar + " " + loader.someArr[0]);
});
So an event on the grid (onViewportChanged) is calling ensureData on the model, which
increments both someVar and someArr[0], both of which are exposed by the return value
of RemoteModel.
After incrementing the event, onDataLoadedSuccess is triggered by the model, however
within that event only the value of loader.someArr[0] has changed.
why is the value of someVar in onDataLoadedSuccess always zero? Is that a scope issue?
And why is the value of someArr[0] changing fine?
How can I properly read the value of someVar in onDataLoadedSuccess?
The RemoteModel function has local variables for someVar and someArr. These are also returned as the object properties when loader is created. The difference is that the value of the number for someVar is effectively copied into loader when it is created.
The array is copied into someArr as well, but the copy is by reference. Thus you have two things pointing to the same array. When the event executes, it updates the local variable and the local array. loader points to the same array, so you see someArr[0] change, but it does not "point to" the number, so when the local someArr is changed the loader value is not.
Edit to address the comment
One option is to define your returned object first and then have your private functions reference it. This could be limited to numbers or apply to all public members; I would do the latter for clarity. With this approach, only the class needs to change, not the code calling it.
function RemoteModel() {
var ret = {
"someArr": [0],
"someVar": 0,
"onDataLoadedSuccess": new Slick.Event()
};
function onSuccess() {
ret.someVar=ret.someVar+100;
// ...
}
return ret;
// ...
A second option is to create a getSomeVar method that is in your returned object so that outside the class you do loader.getSomeVar() instead of just loader.someVar. (And you could create a setSomeVar(newValue) if it needs to be externally settable.
function RemoteModel() {
var someVar=0;
// ...
function getSomeVar() {
return someVar;
}
return {
"someArr": someArr,
"getSomeVar": getSomeVar,
"onDataLoadedSuccess": onDataLoadedSuccess,
};
}
and then:
loader.onDataLoadedSuccess.subscribe(function(e, args) {
console.log(loader.getSomeVar() + " " + loader.someArr[0]);
});
Numbers and other non-object primitives get returned by value, not by reference:
return {
"someArr": someArr, // <-- object
"someVar": someVar, // <-- number
"onDataLoadedSuccess": onDataLoadedSuccess,
};
someArr is an object (array), which will be returned by reference. However someVar is just a simple number. Thereby you're returning a value.
Instead of returning an object in your RemoteModel you should use RemoteModel as a proper constructor:
this.someArr=[0];
this.someVar=0;
this.onDataLoadedSuccess = new Slick.Event();
/* move other functions into the prototype, get rid of the return */
If you're not familiar with using a function as a constructor see Working with Objects on MDN.
It is because how javascript is handling the values.
(function($) {
function RemoteModel() {
this.someArr=[0];
this.someVar=0;
this.onDataLoadedSuccess = new Slick.Event();
var _this = this;
function ensureData(from, to) {
h_request = setTimeout(function() {
req = $.ajax({
url: url,
context: this,
success: function(resp) {
onSuccess();
}
});
}, 3000);
}
function onSuccess() {
_this.someVar=_this.someVar+100;
_this.someArr[0]=_this.someArr[0]+100;
console.log(_this.someVar + " " + _this.someArr[0]); // yes values are changing properly for both !
_this.onDataLoadedSuccess.notify({ from: from, to: to });
}
}
})(jQuery);
When you created a model, you returned this to loader:
return {
"someArr": someArr,
"someVar": someVar,
"onDataLoadedSuccess": onDataLoadedSuccess,
}
loader.someArr is pointing to the same array pointed by someArr in the closure. someVar, however, is a property with a value of 0, copied from someVar in the closure.
When assigning a variable or object property with an object (including arrays), you are referencing to that object. Thus, loader.someArr is pointing to the same array pointed by someArr in the closure. Changes can be done via both someArr and loader.someArr since they point to the same array.
But in the case of someVar, you assigned loader.someVar from someVar. Since 0 is a primitive value, the value is copied from someVar to loader.someVar. Thus, someVar and loader.someVar don't point to the same thing. Modifying someVar does not modify loader.someVar or vice versa.
Thats because at the Moment you return
{
"someArr": someArr,
"someVar": someVar,
"onDataLoadedSuccess": onDataLoadedSuccess,
};
someVar is 0.
someVar holds a String, a primitive type.
someArr references an Array a non Primitive type.
Strings (and other primitive types)
are passed by value, Arrays (and other non-primitive types) by reference. More specific: the variables reference to the Object gets passed by value (when passed the new var holds a copy of the reference)
So what you return is a "copy" of your String

return a value from object in js

Assume I have a simple object in js with one private variable:
function test(){
var value=true;
}
and now I want to create one instance:
var r=new test() //I want to get r === true
How can I return a value from it?
If I write:
function test(){
var value=true;
return value;
}
I have a test {} in result.
If I write:
function test(){
var value=true;
return function(){ return value; }
}
then I can get the value, but I must add additional parentheses:
var r=new test()() //r === true
I don't want the parentheses, so I tried to change the code to:
function test(){
var value=true;
return (function(){ return value; } )();
}
But in response, again I get test {}
How to write the return statement in this situation?
I believe you need to do something like:
function test(){
this.value = true;
}
and then
var r=new test();
if (r.value == true) {
//Do something
}
First I feel obliged to clarify a possible misunderstanding:
function test(){
var value=true;
}
is not an object with a private variable. It is a function with a local variable. When you call the function with new, it creates an object inheriting from the functions's prototype with no properties. If you call the function normally, it simply executes the function body and returns undefined (since you are not returning anything).
Solutions:
Do you actually need a constructor function? I'm asking because your example is very simple. Obviously you cannot have the function return two values, true and the object.
So, you could just call the function without new:
function test() {
var value = true;
return value;
}
var r = test();
If you really want r to be true then I see no reason to call the function as a constructor function.
The reason why you got test {} as result was because you called the function with new. If you do that, the function will always return an object and if you don't do so explicitly (value is a boolean, not an object), it implicitly returns this (which is an object).
So again, if you really want r to be equal to value from inside the function, then simply don't call the function with new.
If you need an object though, there are a couple of ways:
You can assign the value to a property and access it instead, like PokeHerOne showed in his answer or add a function which returns that value, as papaiatis demonstrates. The advantage is that the value is accessed explicitly and other people looking at your code understand what's going on.
Additionally, depending on what you want to do with that value / object, you can implement the valueOf methods, which gets called by various operators.
For example:
function Test(){
var value = true;
this.valueOf = function() {
return value;
}
}
var t = new Test();
console.log(t); // logs the Test instance
console.log(t == true); // logs `true`
I.e. t is an object but behaves like the value true (value) in various operations. This is powerful but can also be quite confusing, since the type conversion is somewhat implicit and it's not something that is used in JavaScript very often.
Used methods defined internally:
function TestClass(){
var value = true;
this.getValue = function(){
return value;
};
}
var t = new TestClass();
alert(t.getValue()); // true
Since value is defined as private it is not accessible from outside:
alert(t.value) // undefined

Can I redefine a JavaScript function from within another function?

I want to pass a function reference "go" into another function "redefineFunction", and redefine "go" inside of "redefineFunction". According to Johnathan Snook, functions are passed by reference, so I don't understand why go() does not get redefined when I pass it into redefineFunction(). Is there something that I am missing?
// redefineFunction() will take a function reference and
// reassign it to a new function
function redefineFunction(fn) {
fn = function(x) { return x * 3; };
}
// initial version of go()
function go(x) {
return x;
}
go(5); // returns 5
// redefine go()
go = function(x) {
return x * 2;
}
go(5); // returns 10
// redefine go() using redefineFunction()
redefineFunction(go);
go(5); // still returns 10, I want it to return 15
​
Or see my fiddle http://jsfiddle.net/q9Kft/
Pedants will tell you that JavaScript is pure pass-by-value, but I think that only clouds the issue since the value passed (when passing objects) is a reference to that object. Thus, when you modify an object's properties, you're modifying the original object, but if you replace the variable altogether, you're essentially giving up your reference to the original object.
If you're trying to redefine a function in the global scope: (which is a bad thing; you generally shouldn't have global functions)
function redefineFunction(fn) {
window[fn] = function() { ... };
}
redefineFunction('go');
Otherwise, you'll have to return the new function and assign on the calling side.
function makeNewFunction() {
return function() { ... };
}
go = makeNewFunction();
Nothing is "passed by reference" in JS. There are times that references are passed, but they're passed by value. The difference is subtle, but important -- for one thing, it means that while you can manipulate a referenced object pretty much at will, you can't reliably replace the object (read: alter the reference itself) in any way the caller will see (because the reference was passed by value, and is thus merely a copy of the original reference; attempting to reassign it breaks in the same way it would if the arg were a number or string).
Some cowboys will assume you're redefining a global function and mess with it by name to get around the limitations of pass-by-value, but that will cause issues the second you decide not to have globals all over the place.
The real solution: return the new function, and let the caller decide what to do with it. (I'd argue that redefining functions right out from under the code that uses them is a pretty bad design decision anyway, but eh. I guess there could be a reason for it...)
Snook is wrong. And I don't think it's pedantic at all (#josh3736 :) to point out that EVERYTHING in JavaScript is pass by value. The article by Snook gets this COMPLETELY wrong. Passing a primitive and passing an object work the exact same way. These are equivalent:
var x = 2;
var y = x;
y = 3; //x is STILL 2.
function stuff(y){
y = 3; //guess what. x is STILL 2
}
stuff(x);
///////////////////
var x = {stuff: 2};
var y = x;
y = {stuff: 3}; //x.stuff is STILL 2
function stuff(y){
y = {stuff: 3}; //guess what. x.stuff is STILL 2
}
stuff(x);
This is important. Java, C#, and MOST languages work this way. That's why C# has a "ref" keyword for when you really do want to pass something by reference.
You can't modify the variable from inside the function, so the quick fix is to return the value and assign it outside the function, like this
// log() just writes a message to the text area
function log(message) {
$('#output').val($('#output').val() + message + "\n");
}
// redefineFunction() will take a function reference and
// reassign it to a new function
function redefineFunction() {
newvalue = function(x) { return x * 3; };
return newvalue;
}
// initial version of go()
function go(x) {
return x;
}
log(go(5)); // returns 5
// redefine go()
go = function(x) {
return x * 2;
}
log(go(5)); // returns 10
// redefine go() using redefineFunction()
go = redefineFunction();
log(go(5)); // returns 10, I want it to return 15
I believe functions are 'passed in by value'. If you put log(f(5)); inside your redefineFunction function it will output 15, but 10 when you call log(go(5)) afterwards.
If you change redefineFunction to return the function and then assign it to go (go = redefineFunction()) it will work as you expect.
This is equivalent to asking if you can redefine any variable by passing it as an argument to some function. No. You can reassign it by, uhh, reassigning it. In this case, if you make redefineFunction return a function, you can simply assign it to go:
function redefineFunction() {
var fn = function(x) { return x * e; };
return fn;
}
function go(x) {
return x;
}
go = redefineFunction();
go(5); // return 15
This is working in firefox:
function redefineFunction(fn) {
window[fn] = function(x) {
return x * 3;
}
};
function go(x) {
return x;
};
alert(go(5));
go=function(x) {
return x * 2;
}
alert(go(5));
redefineFunction('go');
alert(go(5));
The secret is that a global function called go also is called window.go and window["go"].
This can also be used at styles: element.style["overflow"] = "hidden", and in attributes:
element["value"] = "hello there".
This is a very useful knowlege.
Why dont use a object? something like this:
var o = {
go: function( x ) {
return x;
},
redefineFunction: function ( fn ) {
if (typeof fn === 'function') {
this.go = fn;
}
}
}
console.log(o.go(5)); // return 5
var fn = function (x) {
return x * 2;
};
o.redefineFunction(fn);
console.log(o.go(5));​ //return 10
Hope it helps!

Categories