首页 > 解决方案 > 保存对文档片段的引用的函数属性:何时使用 cloneNode?

问题描述

我无法理解对象的数据分配何时是引用以及何时创建对象的副本。我以为我理解了,但下面的例子不符合我对它的简单理解。

事件处理程序开始以下一系列步骤。
Promise.allSettled( [ gather_write, get_build ] ).then( test );

承诺gather_writeget_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必须在 promiseget_build可以解决之前完成,之后Promise.allSettled可以评估;build.html并且,如果两个 promise 都满足,则可以调用将 DOM 节点替换为存储在其中的新建片段的函数。我认为这build.html将是对节点对象的引用,f并且由于f在块中设置为 null finally,这将在上述所有内容完成之前发生,并且当build.html最终运行要使用的代码时,它将指向 null而不是片段。所以,赋值语句应该是build.html = f.cloneNode(true).

但是,无论是否使用f.cloneNode. 你能解释一下为什么吗?如果没有必要,我不希望浏览器采取步骤克隆片段,但是在不理解为什么它在不克隆的情况下工作的情况下犹豫要排除它。

谢谢你。

标签: javascriptdocumentfragment

解决方案


您可以将每个变量名视为指向内存位置的指针。将变量名设置为null不会改变变量曾经包含的任何内容;它所做的只是将变量名指向的内容从先前的引用(此处为文档片段)更改为新的引用(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>;
  }
}

推荐阅读