模块中未更新Nodejs闭包变量

Nodejs closure variable not updated in module

本文关键字:闭包 变量 Nodejs 更新 模块      更新时间:2023-09-26

我需要一些帮助来理解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;

但我仍然想弄清楚为什么第一个模块中的代码不起作用。我正在处理的模块作为一个单例是很好的,所以我很想看看在模块中设置变量的最佳实践是什么,这些变量可以被该模块中的函数更改,并且可以在该模块之外公开访问。

第一种方法不起作用的原因与没有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没有变化。

您认为这是一个对象,而不是一个闭包。当函数有权访问特权数据时,就会产生闭包。也就是说,数据绑定从程序的其余部分"消失"。这里有一个例子:

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()的每次调用都会产生一个新的闭包。

FWIW,如果你这样做,它确实有效:

function someFunction() {
    //someArray = ['blue', 'green'];
    someArray[0] = 'blue'
    someArray[1] = 'green'
}

我认为这意味着当你用[]创建一个新数组时,你不知何故破坏了引用链。我的理解还不足以说明。

访问模块内变量的最佳实践可能是定义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;

获取并设置MDN上的语法定义。

但是,如果您确实希望直接访问对象本身,请使用您提到的模块名称空间:

theModule.someArray = ['blue', 'green'];

但是,您看到的问题是因为您正在用新阵列进行替换,而不是修改阵列,例如

function someFunction() {
    someArray.splice(0);
    someArray.push('blue', 'green');
}

我认为赋值会导致创建一个新的对象引用,而修改现有对象会维护现有引用。

这是共享呼叫的结果:
JavaScript是一种传递引用语言还是传递值语言
https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing