Свойство функции, содержащее ссылку на фрагмент документа: когда использовать cloneNode? - PullRequest
1 голос
/ 12 февраля 2020

Мне трудно понять, когда присвоение данных объекту является ссылкой и когда создается копия объекта. Я думал, что понял, но следующий пример не соответствует моему простому пониманию этого.

Обработчик событий запускает серию шагов следующим образом.
Promise.allSettled( [ gather_write, get_build ] ).then( test );

Обещания gather_write и get_build выполняют несвязанные функции. Первый собирает некоторые данные из DOM и записывает их в базу данных. Второй извлекает данные из базы данных и строит фрагмент документа. Если оба выполняются, то узел в DOM заменяется фрагментом.

Слишком много кода, чтобы показать здесь, но get_build, после успешного получения данных из базы данных, вызывает отдельную функцию, которая создает фрагмент документа, и возвращал свой результат как свойство объекта разрешения. Это работало нормально; и затем я хотел попытаться предоставить опции пользователю в случае, если gather_write отклонено и get_build выполнено, что требует временного хранения фрагмента документа. Таким образом, вместо того, чтобы возвращать его и передавать обратно в Promise.allSettled, он сохраняется в свойстве функции, которая его создает.

В синхронной функции build код задается как:

function build()
  {
    let f;
    try
      {
        f = document.createFragment();
        // ...build the fragment... 
        build.html = f;
      }
     catch(e)
      { }
     finally
      { f = null; }
   } // close build

Функция build должна завершиться до того, как обещание get_build может быть выполнено, после чего Promise.allSettled может быть оценено; и, если оба обещания выполняются, может быть вызвана функция замены узла DOM вновь созданным фрагментом, хранящимся в build.html. Я думал, что build.html будет ссылкой на объект узла f и что, поскольку f установлен в нуль в блоке finally, это произойдет до того, как все вышеперечисленное может завершиться, и когда код чтобы, наконец, использовать build.html, он указывал бы на ноль, а не на фрагмент. Таким образом, оператор присваивания должен быть build.html = f.cloneNode(true).

Однако этот процесс работает нормально с использованием f.cloneNode и без него. Не могли бы вы объяснить, почему? Я не хочу, чтобы браузер предпринял шаги для клонирования фрагмента, если он не нужен, но стесняюсь исключить его, не понимая, почему он работает без клонирования.

Спасибо.

1 Ответ

0 голосов
/ 12 февраля 2020

Вы можете рассматривать каждое имя переменной как указатель на область памяти. Установка имени переменной на null не меняет ничего в том, что переменная раньше содержала; все, что он делает, это изменяет то, на что указывает имя переменной, с предыдущей ссылки (здесь, на фрагмент документа) на новую ссылку (ноль). Таким образом, когда вы делаете

f = document.createFragment();
build.html = f;

, тогда, независимо от того, будет ли / как имя переменной f переназначено в будущем, build.html не будет изменено, поскольку оно продолжает указывать на фрагмент документа , Единственный способ изменить build.html будет, если фрагмент будет мутирован (например, присвоение свойству f перед переназначением или вызов одного из методов фрагмента).

Вот еще один минимальный пример такого поведения:

function foo() {
  let f = { prop: 'val' };
  foo.f = f;
  f = null;
}

foo();

console.log(foo.f);

Опять-таки, здесь, хотя f устанавливается на null, объект { prop: 'val' } остается неизменным и неизменным, поэтому foo.f получает ссылку на этот объект и продолжает удерживать эту ссылку даже после переназначения f.

С вашим кодом, поскольку фрагмент в памяти остается в памяти, а build.html сохраняет ссылку на этот фрагмент даже после f переназначается, клонирование фрагмента просто лишние накладные расходы; не стесняйтесь опускать его.

Другой способ визуализировать это то, что ваш код:

function build() {
  let f;
  try {
    f = document.createFragment();
    // ...build the fragment... 
    build.html = f;
  } catch (e) {} finally {
    f = null;
  }
}

эквивалентен

function build() {
  let f;
  try {
    // Create the object in memory:
    <MemRef#513513> = document.createFragment();
    // Point the variable `f` to that created object:
    f = <MemRef#513513>;
    // ...build the fragment... 
    build.html = <MemRef#513513>;
  } catch (e) {} finally {
    f = <MemRef#Null>;
  }
}
...