Экспорт изменяемых значений: импорт против необходимости

#typescript #ecmascript-6

Вопрос:

Я пытаюсь понять разницу между import и require когда дело доходит до экспорта / импорта изменяемых значений.

Представьте себе файл a.ts:

 export let a = 1;
export function f() {
  a = 2;
}
 

Затем три версии основного файла, index1.ts:

 import { a, f } from "./a";

console.log(a); // 1
f();
console.log(a); // 2
 

индекс 2.ts:

 const { a, f } = require("./a");

console.log(a); // 1
f();
console.log(a); // 1
 

индекс 3.ts:

 const _ = require("./a");

console.log(_.a); // 1
_.f();
console.log(_.a); // 2
 
  • Я ожидал, что index1.ts выдаст тот же результат, что и index2.ts, но это не так. import ed a всегда ссылается на фактическую a переменную из. ts. Каковы гарантии того, что так будет всегда ? Является ли спецификация причиной такого поведения ?
  • index3.ts работает, потому что свойства возвращаемого объекта require являются получателями. Опять же, гарантировано ли, что это всегда будет правдой ? Можем ли мы положиться на это, например, при проектировании библиотеки ?

Комментарии:

1. import создает псевдоним, а не новую переменную. const создает переменную, которая принимает копию значения.

Ответ №1:

Идентификаторы, импортированные с import помощью, могут вести себя странно — они, по-видимому, могут переназначить себя самостоятельно, если модуль, который их экспортирует, переназначает экспортируемое, вот почему

 import { a, f } from './a';
console.log(a);
f()
console.log(a);
 

действительно, может войти 1, а затем 2 — чего вы не увидите ни в каком другом JavaScript.

Является ли спецификация причиной такого поведения ?

Да, это так.

Использование require вместо import , с другой стороны, возвращает объект пространства имен, экспортированный другим модулем, вместо создания переменных модуля со странным поведением. Этот объект работает так, как вы ожидали бы от любого другого объекта в Javascript — если вы деструктурируете значения объекта в переменные, эти значения не «изменятся сами по себе», если вы явно не сделаете что-то подобное a = someOtherValue в текущей области.

index3.ts работает, потому что свойства объекта, возвращаемого require, являются получателями. Опять же, гарантировано ли, что это всегда будет правдой ? Можем ли мы положиться на это, например, при проектировании библиотеки ?

Это немного больше похоже на то, что значения свойств объекта пространства имен переназначаются, когда модуль, из которого они экспортируются, переназначает их. Если у вас есть

 // foo.ts
export let a = 1;
export function f() {
  a = 2;
}
 

тогда объект пространства имен, импортированный в другое место, будет живой структурой, содержащей

 {
  a: currentValueOfAInFoo,
  f: currentValueOfFInFoo,
}
 

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