Josh Black
Josh Black

Reputation: 999

Unexpected behavior from variable reference in a closure

I ran into an issue when I expect a member in a returned object to have the correct value of a variable defined in the parent scope. However, the value of this member never changes and I had to make a getter method to retrieve the correct value. For a trivial example, below is a subset of an Adjacency Matrix representation of a graph:

function AdjacencyMatrix() {
    // Here is the set of Vertices
    var V = [1, 2, 3];

    // Here's some functionality that will remove a vertex at some point,
    // right now we're just concerned with updating what V is equal to
    function removeVertex(v) {
        V.push(4);
        V = [];
        console.log(V);
    }

    // A "getter" method for the list of vertices
    function getVertices() {
      return V;   
    }

    // Ran when the Adjacency Matrix is initialized
    console.log(V);

    return Object.freeze({
        // Member that holds a reference to V
        vertices: V,

        // Methods that will be used later
        removeVertex: removeVertex,
        getVertices: getVertices
    });
}

// Initially logs [1, 2, 3], which is expected
var M = AdjacencyMatrix();

// Logs [], which is expected
M.removeVertex();

// Logs [1, 2, 3, 4], which is unexpected.
// Instead, it should log []
console.log(M.vertices);

// Logs [], which is expected
console.log(M.getVertices());

Shouldn't the vertices member in the returned object always maintain a reference to what the variable V points to? Instead, in this example, accessing the vertices member on M maintains a reference to the array initially assigned to the variable V and ignores any reassignment of variable V.

Or, when assigning the vertices member to V, does it hold a reference to the value of V instead of whatever value the variable has?

Sorry if the wording of this problem is difficult to understand, I tried my best to state my expectations and results.

Upvotes: 1

Views: 52

Answers (3)

JLRishe
JLRishe

Reputation: 101662

M.vertices does not involve a closure.

In an object literal, the right hand side of each key/value pair is evaluated, and the result of that evaluation is what is assigned to each the object's properties.

When this executes:

return Object.freeze({
    // Member that holds a reference to V
    vertices: V,

    // Methods that will be used later
    removeVertex: removeVertex,
    getVertices: getVertices
});

V is evaluated to the array that V is referencing at that moment. So the produced object references that array, but it has no connection to V.


To get the behavior you are describing (if that is what you indeed want). You could define a getter and setter for vertices that do use a closure:

var o = {
    // Methods that will be used later
    removeVertex: removeVertex,
    getVertices: getVertices
}

Object.defineProperty(o, 'vertices', {
    get: function () { return V; },
    set: function (val) { V = val; } // or you can omit the setter
                                     // to make the property readonly
}); 

return Object.freeze(o);

Upvotes: 1

user1840942
user1840942

Reputation: 49

Vertices holds the value of V that was originally assigned to it. If you come from an object oriented language background like C#, this is equivalent to "pass by value" (i.e only the value gets passed and not the reference).

Upvotes: 0

zerkms
zerkms

Reputation: 254906

vertices: V, means:

put a reference to what V refers to to the vertices property.

That's it - it's not a closure, it's just an assignment.

So as soon as you reassign V to refer to something else - the vertices attribute does not reflect that, since it stores an original reference.

Upvotes: 2

Related Questions