【问题标题】:Destructured variable value not persisting between modules解构的变量值不在模块之间保留
【发布时间】:2019-04-02 14:32:42
【问题描述】:

在 Node JS 应用程序的模块之间传递对象时,我得到了不一致的结果,或者可能只是误解了正在发生的事情!

在我的应用程序中,用下面的伪代码编写,首先需要一个 lib 文件到应用程序根目录中,然后是应用程序使用的其他服务,这些服务也需要在同一个库中。然后在应用程序根目录中配置库 - 这会设置一个类变量 (this.foo),该变量在库中初始化为空对象 - 最后调用其中一个服务中的方法。

foo 属性在服务文件的顶部被解构,如果我立即将其注销,我会得到一个空对象(如预期的那样)。当我调用服务方法时,在其他地方配置了 foo 并引用解构的属性后,我仍然得到一个空对象。相反,如果我不解构该属性而只需要在库中 (Lib),那么在我访问 Lib.foo 的方法中,我会看到配置的值(即如预期的那样)。

我怀疑解构变量是一个没有被更新的值,而所需的库是一个引用,但我读过的所有内容都表明没有任何内容是按值传递的。任何指针将不胜感激!

// main.js
// ========
const lib = require('./lib');
const service = require('./service');
lib.configure({ foo: "bar"});
service.run();


// service.js
// ========
const Lib = require('./lib');     // This is for the example only
const { foo } = require('./lib'); // I'd prefer to just do this

console.log(Lib.foo); // {} as expected
console.log(foo);     // {} as expected

class Service {
    run() {
        console.log(foo);    // {} - should be "bar"
        console.log(Lib.foo) // "bar" as expected
    }
}
module.exports = new Service();


// lib.js
// ======
class Lib {
    constructor() {
        this.foo = {};
    }
    configure({ foo }) {
        this.foo = foo;
    }
}
module.exports = new Lib();

【问题讨论】:

    标签: javascript node.js pass-by-reference es6-modules destructuring


    【解决方案1】:

    这是正确的行为。

    = operator (the assignment) 所做的是更新指向包含新值的新内存位置的指针,它不会更改我们当前指向的值。

    对于字符串等原语,一般的实现是在变量重新赋值期间实际复制整个值。

    额外:这通常称为String interning

    这是一个例子:

    var x = "123";  // point the variable named "x" to a memory location containing the string "123"
    var y = x;      // point the variable named "y" to a new memory location into which we copy the data from the memory location that "x" is pointing to
    x = "456";      // point the variable named "x" to a new memory location that that contains the value "456"
    console.log(y); // still "123", "y" still points to a memory location containing the copy of "123"

    这是正在发生的事情的图表。

    var x = "123";
    

    var y = x;
    

    x = "456";
    

    注意:最初的“123”仍然留在内存中,只是没有任何东西指向它,所以garbage collector 最终会清理它。

    在对象的情况下,情况有点不同。我们不是复制值,而是将指针复制到保存该值的内存位置,但重新分配的行为相同。

    var x = {};          // point the variable named "x" to a memory location containing the empty object
    var y = x;           // point the variable named "y" to the same memory location that "x" is pointing to
    x = { name: "foo" }; // point the variable named "x" to a new memory location that contains the object { name: "foo" }
    console.log(y);      // still {}, "y" still points to a memory location containing the empty object

    这是正在发生的事情的图表:

    var x = {};   
    

    var y = x;
    

    x = { name: "foo" };
    

    当您从Lib 的实例中解构foo 时,它与该实例内部的foo 不同,它是一个新变量,指向与内部变量相同的内存位置。此位置包含{}

    当您调用.configure 时,您正在更新内部值指向的内存位置,但解构后的变量仍指向包含{} 的旧位置。

    如果您要更新 foo 指向的对象而不是引用本身,那么一切都会按您的预期工作:

    configure({ foo }) {
        Object.assign(this.foo, foo);
    }
    

    我建议不要像上面那样对您希望在更新期间保持状态的持久 getter 进行解构。拥有这些额外的变量会增加复杂性(因为它会增加要维护的引用数量)并且还可能导致memory leaks(过时的引用指向未使用的数据,这会阻止垃圾收集)。

    此外,从类中解构方法可能会导致上述错误以及this 的意外行为。

    如果你总是打电话给Lib.foo,你会省去一些麻烦。

    【讨论】:

    • 感谢您如此深入的回复,以及更多让我能理解的回复!我将坚持使用 Lib.foo 方法而不是更新对象,但值得牢记。
    • 很高兴帮助队友?
    【解决方案2】:

    所以我认为你混淆了参考和价值观。 { foo } 的值不会改变而 Lib.foo 的值会改变的原因是因为在 foo 的情况下,您正在分配 Lib.foo 值的副本当时。如果您想改变 Lib 的数据并使其传播,您将需要使用该对象(而不是像您当前所做的那样解构参数)。

    希望这会有所帮助!

    【讨论】:

    • 感谢 Jack - 与 nem 相同且更易于消化,尽管由于额外的细节而接受了 nems 的回答。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-23
    • 1970-01-01
    • 2023-02-01
    • 2012-08-18
    相关资源
    最近更新 更多