Reputation: 999
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
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
.
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
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
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