我有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
错误。
答案 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'