闭包中变量引用的意外行为

时间:2014-12-23 19:47:29

标签: javascript closures

当我希望返回对象中的成员具有父作用域中定义的变量的正确值时,我遇到了一个问题。但是,此成员的值永远不会更改,我必须使用getter方法来检索正确的值。对于一个简单的例子,下面是图的Adjacency Matrix表示的子集:

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());

返回对象中的vertices成员是否应始终保持对变量V指向的引用?相反,在此示例中,访问vertices上的M成员会维护对最初分配给变量V的数组的引用,并忽略变量V的任何重新分配。

或者,在将vertices成员分配给V时,它是否包含对V的值的引用,而不是该变量的值是什么?

很抱歉,如果难以理解这个问题的措辞,我会尽力说出我的期望和结果。

3 个答案:

答案 0 :(得分:2)

vertices: V,表示:

引用V引用vertices属性的内容。

就是这样 - 它不是一个闭包,它只是一个任务。

因此,只要您重新指定V以引用其他内容,vertices属性就不会反映出来,因为它会存储原始引用。

答案 1 :(得分:1)

M.vertices不涉及关闭。

在对象文字中,每个键/值对的右侧都是评估,并且该评估的结果是分配给每个对象属性的结果。

执行此操作时:

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

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

V将被评估为V此时引用的数组。因此,生成的对象引用该数组,但它与V无关。

<小时/> 获取您描述的行为(如果这是您确实想要的)。您可以为使用闭包的vertices定义一个getter和setter:

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);

答案 2 :(得分:0)

顶点保存最初分配给它的V值。如果你来自像C#这样的面向对象语言背景,这相当于&#34;传递值&#34; (即只传递值而不是参考)。