Значение разрушенной переменной не сохраняется между модулями

1

Я получаю несогласованные результаты при передаче объектов между модулями в приложении 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();
Теги:
destructuring
pass-by-reference
es6-modules

2 ответа

1
Лучший ответ

Это правильное поведение.

Что делает оператор = (оператор присваивания), он обновляет указатель на новую ячейку памяти, содержащую новое значение, это не изменяет значение, на которое мы сейчас указываем.

В случае примитивов, таких как строки, общая реализация заключается в фактическом копировании всего значения во время перераспределения переменной.

Дополнительно: обычно это называется 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";

Изображение 174551

var y = x;

Изображение 174551

x = "456";

Изображение 174551

Примечание: исходный "123" по-прежнему остается в памяти, но ничего больше не указывает на него, поэтому сборщик мусора в конечном итоге очистит его.

В случае объектов ситуация немного отличается. Вместо копирования значения мы копируем указатель на ячейку памяти, удерживая это значение, но повторное присвоение ведет себя одинаково.

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 = {};   

Изображение 174551

var y = x;

Изображение 174551

x = { name: "foo" };

Изображение 174551

Когда вы destructure foo из экземпляра Lib, это не то же самое foo, как один внутренний в этом случае, это новая переменная, указывающая на то же место памяти, как внутренней переменной. Это место содержит {}.

Когда вы вызываете .configure, вы обновляете место памяти, в котором указывается внутреннее значение, но разрушенная переменная все еще указывает на старое местоположение, содержащее {}.

Если вы обновите объект, на который указывает foo, вместо самой ссылки, все будет работать так, как вы ожидали:

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

Я бы посоветовал не делать деструктурирования для постоянных геттеров, как и выше, что вы ожидаете сохранить состояние через обновления. Наличие дополнительных переменных увеличивает сложность (потому что увеличивает количество ссылок на поддержку), а также может привести к утечке памяти (устаревшие ссылки указывают на неиспользуемые данные, которые предотвращают сбор мусора).

Кроме того, методы деструктурирования из класса могут приводить к ошибкам, подобным выше, а также к непреднамеренному поведению this.

Вы спасете себе несколько головных болей, если вы всегда будете называть Lib.foo.

  • 0
    Спасибо за такой всесторонний ответ и еще один, который я мог понять! Я буду придерживаться подхода Lib.foo, а не обновлять объект, но стоит иметь в виду.
0

Поэтому я думаю, что вы смешиваете ссылки и ценности. Причина, по которой значение {foo} не изменяется, а значение Lib.foo - потому, что в случае foo вы назначаете копию значения Lib.foo в это время. Если вы хотите изменить данные Lib и распространить их, вам нужно будет работать с объектом (а не деконструировать аргумент, как вы сейчас делаете).

Надеюсь это поможет!

  • 0
    Спасибо, Джек, - тот же момент, что и nem, и его легче переварить, хотя принятые nems отвечают из-за дополнительных деталей.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню