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