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闭包问题

闭包的高级使用-变量私有化-模块化开发-js命名空间

js中的闭包之我理解

JS你不知道的JavaScript 笔记—— 作用域与闭包 - 编译原理 - LHS - RHS - 循环与闭包 - 模块 - 词法作用域 - 动态作用域

JS函数的应用 --- 立即执行函数全局污染闭包沙箱递归

nodejs 闭包函数可以传参数吗