Nodejs闭包变量未在模块中更新
Posted
技术标签:
【中文标题】Nodejs闭包变量未在模块中更新【英文标题】:Nodejs closure variable not updated in module 【发布时间】:2016-02-21 19:43:00 【问题描述】:我需要一些帮助来理解 NodeJ。我显然错过了一些基本的东西。我有一个类似于以下的模块,使用基本的显示模块模式......
var someArray = [];
var publicApi =
someArray: someArray,
someFunction: someFunction
;
function someFunction()
someArray = ['blue', 'green'];
module.exports = publicApi;
当我使用这个模块时,调用 someFunction 时 someArray 没有改变...
var myModule = require('myModule');
myModule.someFunction();
var test = myModule.someArray;
// test is always an empty array
请帮助我理解原因。我意识到我可能可以使用构造函数让它工作,但我想填补我对上述为什么不起作用的知识的空白。
更新:
我可以通过对模块的以下修改来实现它...
var theModule =
someArray: [],
someFunction: someFunction
;
function someFunction()
theModule.someArray = ['blue', 'green'];
module.exports = theModule;
但我仍然想确切地了解为什么第一个模块中的代码不起作用。我正在开发的模块作为一个单例来说很好,所以我很想看看在模块中包含变量的最佳实践是什么,这些变量可以由该模块中的函数更改,并且可以在该模块之外公开访问。
【问题讨论】:
这里有一个拼写错误:someArray = ['blue', 'green'];
已更正。是的,这是我将实际代码降至最低限度以显示问题。我在我的真实代码中使用了对象。
【参考方案1】:
第一种方法不起作用的原因与在没有 Node 的 javascript 中做同样的事情不起作用的原因相同:
var someArray = [];
var object =
someArray: someArray,
someArray = [1, 2, 3];
console.log(object.someArray);
这将打印[]
,因为object.someArray
是对您创建的第一个数组的引用。这是过程:
var someArray = [];
创建一个空数组,然后将该数组的引用保存为名称someArray
。我们称之为array1
。
var object =
someArray: someArray
使用属性someArray
创建一个对象,使该属性引用数组someArray
引用。重要的是要知道这意味着此引用现在是对 array1
的引用,而不是对 someArray
的引用。这导致我们:
someArray = [1, 2, 3];
这会创建一个新数组(我们称之为array2
),然后将其存储为someArray
。这个数组完全独立于array1
并且someArray
的所有未来 引用都会得到array2
,但它对之前的访问没有影响。
这与您的 Node 示例完全相同 - 当您覆盖 someArray
而不是 publicApi.someArray
时,您不会对 publicApi.someArray
进行任何更改,因此您不能指望它会有所不同。
希望能清楚地说明这一点,您可以从以下内容开始:
someArray -> array1[]
object.someArray -> array1[]
到这里:
someArray -> array2[1, 2, 3]
object.someArray -> array1[]
注意object.someArray
没有改变。
【讨论】:
this和csum的回答都是准确的,但是这个更清楚一点。我的解释是我的 publicApi.someArray 仍然指向原始的空数组,它完全没有被语句 someArray = ['blue', 'green'] 改变。该语句更改了 someArray 指向的内容,但不会更改 publicApi.someArray 指向的内容。【参考方案2】:您将其视为对象而不是闭包。当函数可以访问特权数据时,会产生闭包。也就是说,数据绑定从程序的其余部分“消失”。这是一个例子:
function SomeFunction(x)
var closureVar = x;
toReturn = function(y)
var answer = closureVar;
closureVar = y;
return answer;
return toReturn;
var runIt = SomeFunction(15);
发生的情况是,当调用 SomeFunction 时,它会为“closureVar”创建一个本地绑定。当一个函数退出时,它的所有本地绑定(通常)都会消失。但是,在这种情况下,返回的函数包含对“closureVar”的引用,因此不能完全删除。因此,定义为 'toReturn' 的函数仍然可以使用它。
但是,程序中没有其他任何东西可以得到它。如果再次调用 SomeFunction(),它将为closureVar 创建一个新的(本地)绑定,该绑定将由调用 SomeFunction() 给出的 toReturn 函数使用。每次调用 SomeFunction() 都会产生一个新的闭包。
【讨论】:
这对闭包很有意义,但也许我的问题与闭包无关。也许我写的问题标题不正确。我的问题更多地针对 Node 模块,也许我创建的根本不是闭包。因此我的困惑。我需要了解 Node 对我上面写的代码做了什么。 蒂姆,这仍然是一个变量范围问题。您尝试更改的数组的全名是“MyModule.someArray”。您的函数正在创建一个名为“someArray”的新局部变量。如果你希望它引用你在模块中定义的 someArray,你需要使用MyModule.someArray = ['blue', 'green'];
这不正确。如果我将 someArray 的声明更改为 var someArray = ['yellow'];然后当我调试 someFunction 时, someArray 将以 ['yellow'] 的值开始。此外,在 WebStorm 中,someArray 列在“闭包”变量下,而不是“本地”变量下,因此它肯定与初始变量相关联。欢迎您尝试一下。
@TimHardy 是的,someArray
可以从publicApi
中完全访问,但它只用于初始化 publicApi.someArray
,它与someArray
没有不可分割的联系. someArray
引用的数组的任何更改都将反映在publicApi.someArray
中,因为它们目前引用同一个数组。但是,当您执行someArray = [...]
时,您使someArray
成为一个不同 数组,并且您不能再通过someArray
更改publicApi.someArray
的值。我试图在我的回答中澄清。【参考方案3】:
FWIW,如果你这样做,它确实有效:
function someFunction()
//someArray = ['blue', 'green'];
someArray[0] = 'blue'
someArray[1] = 'green'
我认为这意味着当您使用 [] 创建一个新数组时,您会以某种方式破坏引用链。我的理解还不够好。
【讨论】:
【参考方案4】:在模块中访问变量的最佳实践可能是定义 get/set 方法:
var someArray = [];
var publicApi =
getSomeArray: function () return someArray; ,
setSomeArray: function (s) someArray = s; ,
/*
* Or if you know you can support get/set syntax:
*
* get someArray () return someArray;
* set someArray (s) someArray = s;
*/
someFunction: someFunction
;
function someFunction()
someArray = ['blue', 'green'];
module.exports = publicApi;
Get 和 set 在 MDN 上的语法定义。
但如果您确实想直接访问对象本身,请使用您提到的模块命名空间:
theModule.someArray = ['blue', 'green'];
但是您看到的问题是因为您要替换为新数组,而不是修改数组,例如
function someFunction()
someArray.splice(0);
someArray.push('blue', 'green');
我相信赋值会导致创建新的对象引用,而修改现有对象会维护现有引用。
这是共享呼叫的结果:Is JavaScript a pass-by-reference or pass-by-value language?https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing
【讨论】:
以上是关于Nodejs闭包变量未在模块中更新的主要内容,如果未能解决你的问题,请参考以下文章
JS你不知道的JavaScript 笔记—— 作用域与闭包 - 编译原理 - LHS - RHS - 循环与闭包 - 模块 - 词法作用域 - 动态作用域