【问题标题】:Weird behavior with variable scope and 'require' in NodeJSNodeJS 中变量范围和“要求”的奇怪行为
【发布时间】:2017-02-27 04:23:46
【问题描述】:

我有 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 的错。

【问题讨论】:

  • 您已将a 定义为一个对象。对象包含键值对。所以,试着在你的第一个例子中写a={b: 'test'}

标签: javascript node.js scope


【解决方案1】:

这是一个指针。在 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 指向一个函数,2. 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'

【讨论】:

    【解决方案2】:

    foo 可以修改变量a 指向别的东西。

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

    在您的第二个示例中,您没有将a 分配给新对象,而是正在修改现有对象(通过添加新字段)。当然,任何曾经获得过该对象的人都可以看到它。

    这(非常粗略)类似于

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多