jcaddy
jcaddy

Reputation: 501

Javascript closures - lifetime of variables

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

Answers (3)

Felix Kling
Felix Kling

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

Guilherme Sehn
Guilherme Sehn

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

Ry-
Ry-

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

Related Questions