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

Javascript 闭包中变量引用的意外行为,javascript,closures,Javascript,Closures,当我期望返回对象中的成员具有在父范围中定义的正确变量值时,我遇到了一个问题。但是,这个成员的值从未更改,我必须使用getter方法来检索正确的值。对于一个简单的示例,下面是图的邻接矩阵表示的子集: function AdjacencyMatrix() { // Here is the set of Vertices var V = [1, 2, 3]; // Here's some functionality that will remove a vertex at s

当我期望返回对象中的成员具有在父范围中定义的正确变量值时,我遇到了一个问题。但是,这个成员的值从未更改,我必须使用getter方法来检索正确的值。对于一个简单的示例,下面是图的邻接矩阵表示的子集:

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());
返回对象中的
顶点
成员不应该始终维护变量
V
指向的引用吗?相反,在本例中,访问
M
上的
顶点
成员会保留对最初分配给变量
V
的数组的引用,并忽略变量
V
的任何重新分配

或者,当将
顶点
成员指定给
V
时,它是否引用了
V
的值,而不是变量的任何值


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

顶点:V,表示:

V
所指的内容引用到
顶点
属性

就这样-这不是结束,这只是一个任务


因此,只要重新分配
V
以引用其他内容,
顶点
属性不会反映这一点,因为它存储原始引用。

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
没有连接


获得你描述的行为(如果这是你真正想要的)。可以为使用闭包的
顶点定义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);

顶点保留最初指定给它的V值。如果您来自面向对象的语言背景,如C#,这相当于“按值传递”(即只传递值而不传递引用)

我喜欢这个解释!但是我有一个问题,为什么
M.vertices
的值会更新为
[1,2,3,4]
,而不是停留在
[1,2,3]
?在
removeVertex
方法中,我将值4推到
m
上,当我访问
m.vertex
时,这一变化会反映出来。因为您仍然持有一个引用,所以您可以变异引用的对象并观察到变异。zerkms是这么说的。计算表达式
V
不会创建全新的数组。它生成对
V
当时正在引用的数组的引用。如果之后修改了该数组,那么当您通过
M.vertices
访问该数组时,它将被反映出来。为什么不是这样?虽然从技术上讲,它不是“传递值”,但从概念上讲,以这种方式理解它更容易。V的“值”被指定给顶点,而不是它的引用。它在任何方面都不是“传递值”。完全一样。它有一个特殊的名字“共享呼叫”。“V的‘值’被分配给顶点,而不是它的引用。”---这是完全错误的,因为它正好相反:分配给它的是引用。我仍然不明白。如果它是指定给顶点的引用,那么为什么当V发生变化时它不被更新呢?M.removeVertex();不变异吗?如果它是由M.removeVertex()变异的;然后是console.log(M.vertices);应该记录[]而不是[1,2,3,4]