Reputation: 501
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.
Upvotes: 0
Views: 1049
Reputation: 816462
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.
Upvotes: 1
Reputation: 6787
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
Upvotes: 4
Reputation: 224942
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.
Upvotes: 1