变量范围的奇怪行为和'要求'在NodeJS中

时间:2017-02-27 04:23:47

标签: javascript node.js scope

我有2个文件:

testrequire.js

let a = {};

function foo() {
    a = 'test';
 };

module.exports.foo = foo;
module.exports.a = a;

test.js

let a = require('./testrequire');

a.foo();

console.log(a);

当我运行test.js时,结果如下:

{ foo: [Function: foo], a: {} }

但我期待它是这样的:

{ foo: [Fuction: foo], a: 'test' }

但是,当我像这样更改 testrequire.js 时:

let a = {};

function foo() {
    a.b = 'test';
};

module.exports.foo = foo;
module.exports.a = a;

结果是:

{ foo: [Function: foo], a: { b: 'test' } }

这完全像我的预期。

这里的问题是:为什么功能foo()可以修改a的属性,但无法修改a

P / S:我确实尝试var而不是let,结果仍然相同。所以绝对不是ES6 let错误。

2 个答案:

答案 0 :(得分:2)

foo 可以修改变量a以指向其他内容。

但这对导出的对象没有影响。完成require后,调用模块将接收当时指向的a。之后,它不关心(变量)a会发生什么。

在第二个示例中,您没有将a分配给新对象,而是修改现有对象(通过添加新字段)。当然,任何以前掌握过该物体的人都可以看到这一点。

这(非常粗略地)类似于

function callerDoesNotSeeThis(a){  a = 1 }

function callerSeesThis(a){  a.foo = 1 }

答案 1 :(得分:2)

它是指针的东西。它在C / C ++,Java等中是相同的。我们已经习惯了闭包,我们有点期望常规指针同样工作。但指针/引用是简单的间接。

让我们来看看您的代码:

let a = {};

创建一个对象({})并将变量a指向该对象。

function foo() {
    a = 'test';
};

声明一个函数foo(),用字符串覆盖a的值。现在,如果你还记得你的C /汇编,那么你应该记住指针的值是它指向的东西的地址。因此a的原始值不是{},而是该对象的地址。当你用一个字符串覆盖a时,该对象仍然存在,并且可以被垃圾收集,除非其他东西指向它。

module.exports.foo = foo;
module.exports.a = a;

导出两个属性,1。foo指向一个函数,而a指向a指向的同一对象。请记住,就像在C / Java中一样,这并不意味着module.exports.a指向a,而是指向{}。现在,您有两个指向同一对象{}的变量。

现在,当你这样做时:

a.foo();

您所做的只是将包含的变量a更改为指向字符串而不是原始对象。你根本没有对a.a做任何事情。它仍然指向{}

变通方法

有两种方法可以获得你想要的东西。首先,OO方式。不要为a创建闭包,使其成为常规对象属性:

function foo() {
  this.a = 'test';
};
module.exports.foo = foo;
module.exports.a = {};

这将按预期工作,因为node.js中的模块是正确的单例,因此可以将它们视为常规对象。

第二种方法是使用getter获取封闭的a。请记住,闭包只适用于函数,而不是对象。因此,只需将变量赋值给属性,就会导致常规指针操作而不是闭包。解决方法是:

let a = {};

function foo() {
  a = 'test';
};
function getA() {
  return a; // this works because we've created a closure
}
module.exports.foo = foo;
module.exports.getA = getA;

现在你可以做到:

a.foo();
a.getA(); // should return 'test'