Nodejs闭包变量未在模块中更新

时间:2015-11-19 15:16:31

标签: javascript node.js closures

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

但我仍然想明白为什么第一个模块中的代码不起作用。我正在处理的模块很好,作为一个单例,所以我很乐意看到在模块中有变量的最佳实践,可以通过该模块中的函数进行更改,并且可以公开访问在那个模块之外。

4 个答案:

答案 0 :(得分:2)

你第一次使用它的方法不起作用的原因与在没有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。此数组完全独立于array1someArray的所有未来引用都将获得array2,但它对之前的访问没有任何影响。

这与您的Node示例完全相同 - 当您覆盖someArray而不是publicApi.someArray时,您不会对publicApi.someArray进行任何更改,因此您不能指望它与

为了清楚地表明这一点,请从以下内容出发:

someArray -> array1[]
object.someArray -> array1[]

对此:

someArray -> array2[1, 2, 3]
object.someArray -> array1[]

请注意object.someArray未更改。

答案 1 :(得分:0)

你正在考虑这个问题,好像它是一个对象而不是一个闭包。当函数可以访问特权数据时,会产生闭包。也就是说,数据绑定"消失"从该计划的其余部分。这是一个例子:

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

答案 2 :(得分:0)

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

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

我认为这意味着当你使用[]创建一个新数组时,你会以某种方式打破引用链。我的理解还不够好。

答案 3 :(得分:0)

访问模块中变量的最佳做法可能是定义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上的

Getset语法定义。

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

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