ホームページ ウェブフロントエンド jsチュートリアル JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

Aug 05, 2021 pm 02:58 PM

多くの開発者は、自分が管理するページにメモリ リークがあるかどうかを通常は気にしないかもしれません。その理由は、単純なページのメモリ リークは最初は非常に遅く、深刻なフリーズを引き起こす前にユーザーによって更新される可能性があるためと考えられます。問題も隠されていましたが、ページがますます複雑になるにつれて、特にページが SAP モードで対話する場合には、メモリ リークの隠れた危険性がますます深刻になり、ある日突然、ユーザーが次のように報告しました。 」

この記事では、いくつかの簡単な例を通じてメモリ リークの調査方法を紹介し、メモリ リークの原因と一般的な状況をまとめ、状況ごとにメモリ リークを回避する方法をまとめます。それが私たちを助けることができることを願っています。

おすすめビデオ: 「JavaScript 基本チュートリアル

1. 簡単な例

1 つ見てみましょう最初に簡単な例を示します。この例に対応するコードは次のとおりです。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>memory-leak</title>
  </head>
  <body>
    <p>push date for <button>0</button> times</p>
    <p>add Date: <button>add date</button></p>
    <p>clear: <button>clear</button></p>
    <script>
      const pushDate = document.querySelector(".push-date");
      const dateCount = document.querySelector(".count-date");
      let dateAry = [];
      let dateNum = 0;
      // 【写入 date】
      pushDate.addEventListener("click", () => {
        dateCount.innerHTML = `${++dateNum}`;
        for (let j = 0; j < 3000; ++j) {
          dateAry.push(new Date());
        }
      });
      const clear = document.querySelector(".clear");
      // 【回收内存】
      clear.addEventListener("click", () => {
        dateAry = [];
        dateCount.innerHTML = "0";
      });
</script>
  </body>
</html>
ログイン後にコピー

コード 1

コード 1 のロジックは非常に単純です。「日付を追加」ボタンをクリックすると、3000新しい項目は dateAry 配列にプッシュされます。Date オブジェクトの場合、「クリア」ボタンをクリックすると dateAry がクリアされます。明らかに、「日付の追加」操作によりメモリ使用量が増加し続けます。このロジックを実際のアプリケーションで使用すると、(このようなコード ロジックの意図的な設計に関係なく) メモリ リークが発生します。メモリ増大の原因とメモリ リークを見つける方法を調べてください。

1 ヒープ スナップショット

ブラウザ プラグインからの干渉を避けるために、Chrome で新しいシークレット ウィンドウを作成し、上記のコードを開きます。次に、Chrome の開発ツールのメモリ ツールで「ヒープ スナップショット」ツールを見つけ、左上隅の記録ボタンをクリックしてスナップショットを記録し、次に「日付の追加」ボタンをクリックして、GC (ガベージ コレクト) を手動でトリガーした後、記録します。図 1 に示すように、上記の操作を数回繰り返して、一連のスナップショットを取得します。

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

図 1 スナップショットの記録

図 2 は、取得したばかりのスナップショット グループです。最初のスナップショットは、ページが最初に読み込まれたときに記録されました。これは困難です。 2 番目のスナップショットから開始すると、各スナップショットのサイズが前のスナップショットと比較して約 200 KB 増加していることがわかります。スナップショット 2 をクリックして選択し、クラス フィルターの入力ボックスに日付を入力します。スナップショット内のすべての日付構成を取得できます。 2. コンストラクタによって構築される JS オブジェクトは Date オブジェクトですが、ここで見られるコンストラクタはブラウザの内部実装に関連するものであり、必ずしも JS オブジェクトに対応するわけではありません。

日付オブジェクトを選択します。下のパネルでは、選択したオブジェクトの保持チェーンと、関連する保持オブジェクトのメモリの保持サイズ (保持サイズ) が表示されます。選択した日付は、図. オブジェクトは Array の最初の要素 (インデックスは 0 から始まります), そしてこの Array の保持者は system/Context コンテキストの dateAry です. system/Context コンテキストはコード内の script タグのコンテキストです.この dataAry の予約サイズは 197 KB であることがわかります。次に、スナップショット 3 に切り替えて、同じ方法でメモリ保持量とサイズを確認します。スナップショット 3 の dataAry の予約サイズが 386 KB になっていることがわかります。 Snapshot 2 と比較して約 200KB です。後続のスナップショット 4 と 5 を 1 つずつ比較すると、同じ比較結果が得られます。つまり、次のスナップショットの dateAry は、前のスナップショットの予約サイズよりも約 200KB 大きくなります。

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

図 2 記録されたスナップショット グループ

[コード 1] を参照してください。「日付の追加」ボタンをクリックすると、日付が追加されることがわかります。 dateAry 配列 3000 個の新しい Date オブジェクトがプッシュされます。これらの 3000 Date オブジェクト (Date x 3000) は、図 2 の Date コンストラクターの右側に表示されます。これらは、ループによって作成された 3000 個の Date オブジェクトに対応します。上記の操作に基づいて、chorome devtools の Memroy のヒープ スナップショット ツールは、特定の瞬間のすべてのメモリ オブジェクトを記録できることがわかります。これは「スナップショット」です。スナップショットは「コンストラクター」によってグループ化され、記録されたすべてのオブジェクトが表示されます。物体。

このページが実際にユーザーにサービスを提供する Web サイトのページである場合 (ユーザーが「日付を追加」ボタンを頻繁にクリックする可能性がある)、作成者はユーザーのクリック数を記録したいと思うかもしれません。おそらく、私もそう思いますが、ユーザーの使用時間が長くなると、「日付を追加」ボタンの反応がどんどん遅くなり、ページ全体がどんどん固まっていきます。その理由は、システムの不具合だけではありません。メモリ リソースが占有され、図 3 に示すように、GC の頻度と継続時間が増加します。GC の実行中は JS の実行が中断されるため、ページはますますスタックしているように見えます。

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

图 3 Performance 录制的 GC 占比

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

图 4 chrome 的任务管理器

最终:

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

图 5 内存占用过高导致浏览器崩溃

那么,在这个“实际”的场景下,如何找出那“作祟”的 3000 个 Date 对象呢?我们首先想到的应该是就是:之前不是录制了好多个 Snapshot 吗?可不可以把它们做对比找到“差异”呢,从差异中找到增长的地方不就行了?思路非常正确,在此之前我们再分析一下这几个 Snapshot:每次点击“add date”按钮、手动触发 GC、得到的 Snapshot 的大小相比上一次都有所增加,如果这种内存的增长现象不符合“预期”的话(显然在这个“实际”的例子中是不符合预期的),那么这里就有很大的嫌疑存在内存泄漏。

这个时候我们选中 Snapshot 2,在图 2 所示的 " Summary" 处选择“Comparison”,在右侧的 "All objects" 处选择 Snapshot 1,这样一来,Constructor 里展示便是 Snapshot 1 和 Snapshot 2 的对比,通过观察不难发现,图中的 +144KB 最值得怀疑,于是我们选中它的构造器 Date,展开选中任意子项看详情,发现其是被 Array 构造器构造出来的 dateAry 持有的(即 dateAry 中的一员),并且 dateAry 被三个地方持有,其中系统内部的 array 我们不用理会,图 6 中写有 "context in ()" 地方给了我们持有 dateAry 的 context 所在的位置,点击便可以跳到代码所在的位置了,整个操作如图 6 所示:

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

图 6 定位代码位置

这里有一个值得注意的地方,图 6 中的 “context in () @449305” 中的 "()",这里之所以展示为了 "()" 是因为代码中用了“匿名函数”(代码 2 中第 2 行的箭头函数):

// 【写入 date】
pushDate.addEventListener("click", () => {
    dateCount.innerHTML = `${++dateNum}`;
    for (let j = 0; j < 3000; ++j) {
        dateAry.push(new Date());
    }
});
ログイン後にコピー

代码 2 匿名函数

但是如果我们给函数起一个名字,如下面的代码所示,也就是如果我们使用具名函数(代码3 第 2 行函数 add)或者将函数赋值给一个变量并使用这个变量(第 10 和 18 行的行为)的时候,devtools 中都可以看到相应的函数的名字,这也就可以帮助我们更好的定位代码,如图 7 所示。

// 【写入 date】
pushDate.addEventListener("click", function add() {
    dateCount.innerHTML = `${++dateNum}`;
    for (let j = 0; j < 3000; ++j) {
        dateAry.push(new Date());
    }
});
const clear = document.querySelector(".clear");
const doClear = function () {
    dateAry = [];
    dateCount.innerHTML = "0";
};
// 【回收内存】
clear.addEventListener("click", doClear);
ログイン後にコピー

代码 3 具名函数

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

图 7 具名函数方便定位

这样我们便找到了代码可疑的地方,只需要将代码的作者抓过来对着他一顿“分析”这个内存泄漏的问题基本就水落石出了。

其实,Snapshot 除了“Comparison”之外还有一个更便捷的用于对比的入口,在这里直接可以看到在录制 Snapshot 1 和 Snapshot 2 两个时间点之间被分配出来的内存,用这种方式也可以定位到那个可疑的 Date x 3000:

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

图 8 Snapshot 比较器

上文件介绍的是用 Heap Snapshot 寻找内存泄漏点的方法,这个方法的优点:可以录制多个 Snapshot,然后方便的两两比较,并且能看到 Snapshot 中的全量内存,这一点是下文要讲的“Allocation instrumentation on timeline”方法不具备的,并且这种方法可以更加方便地查找后面会讲的因 Detached Dom 导致的内存泄漏。

2 Allocation instrumentation on timeline

但是,不知道你有没有觉得,这种高频率地录制 Snapshot、对比、再对比的方式有点儿麻烦?我需要不断的去点击“add date”,然后鼠标又要跑过去点击手动 GC、录制 Snapshot、等待录制完毕,再去操作,再去录制。有没有简单一些的方式来查找内存泄漏?这个时候我们回到 Memory 最初始的界面,你突然发现 “Heap snapshot”下面还有一个 radio:“Allocation instrumentation on timeline”,并且这个 radio 下面的介绍文案的最后写着:“Use this profile type to isolate memory leaks”,原来这是一个专门用于调查内存泄漏的工具!于是,我们选中这个 radio,点击开始录制按钮,然后将注意力放在页面上,然后你发现当点击“add date”按钮时,右面录制的 timeline 便会多出一个心跳:

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

图 9 Allocation instrumentation on timeline

如图 9 所示,每当我们点击“add date”按钮时,右面都有一个对应的心跳,当我们点击“clear”按钮时,刚才出现的所有心跳便全都“缩回”去了,于是我们得出结论:每一个“心跳”都是一次内存分配,其高度代表内存分配的量,在之后的时间推移过程中,如果刚才心跳对应的被分配的内存被 GC 回收了,“心跳”便会跟着变化为回收之后的高度。于是,我们便摆脱了在 Snapshot 中来回操作、录制的窘境,只需要将注意力集中在页面的操作上,并观察哪个操作在右边的时间线变化中是可疑的。

经过一系列操作,我们发现“add date”这个按钮的点击行为很可疑,因为它分配的内存不会自动被回收,也就是只要点击一次,内存就会增长一点,我们停止录制,得到了一个 timeline 的 Snapshot,这个时候如果我们点击某个心跳的话:

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

图 10 点击某个心跳

熟悉的 Date x 3000 又出现了(图 11),点击一个 Date 对象看持有链,接下来便跟上文 Snapshot 的持有链分析一样了:

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

图 11 通过 timeline 找到泄漏点

这个方法的优点上文已经说明,可以非常直观、方便的观察内存随可疑操作的分配与回收过程,可以方便的观察每次分配的内存。它的缺点:录制时间较长时 devtools 收集录制结果的时间会很长,甚至有时候会卡死浏览器;下文会讲到 detached DOM,这个工具不能比较出 detached DOM,而 heap snapshot 可以。

3 performance

devtools 中的 Performance 面版中也有一个 Memory 功能,下面看一下它如何使用。我们把 Memory 勾选上,并录制一个 performance 结果:

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

图 12 Performance 的录制过程

在图 12 中可以看到,在录制的过程中我们连续点击“add date”按钮 10 次,然后点击一次“clear”按钮,然后再次点击“add date” 10 次,得到的最终结果如图 13 所示:

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

图 13 Performance 的录制结果

在图 13 中我们可以得到下面的信息:

整个操作过程中内存的走势:参见图 13 下方的位置,第一轮点击 10 次的过程中内存不断增长,点 clear 之后内存断崖式下跌,第二轮点击 10 次内存又不断增长。这也是这个工具的主要作用:得到可疑操作的内存走势图,如果内存持续走高则有理由怀疑此操作由内存泄漏的可能。

内存的增长量:参见 JS Heap 位置,鼠标放上去可以看见每个阶梯上下位置的内存增长/下跌的量

通过在 timeline 中定位某个“阶梯”,我们也能找到可疑的代码,如图 14 所示:

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

图 14 通过 Performance 定位问题代码

这种方法的优点:可以直观得看到内存的总体走势,并且同时得到所有操作过程中的函数调用栈和时间等信息。缺点:没有具体的内存分配的细节,录制的过程不能实时看到内存分配的过程。

二 内存泄漏出现的场景

1 全局

JS 采用标记清扫法去回收无法访问的内存对象,被挂载在全局对象(在浏览器中即指的是 window 对象,在垃圾回收的角度上称其为根节点,也叫 GC root)上的属性所占用内存是不会被回收的,因为其是始终可以访问的,这也符合“全局”的命名含义。

解决方案就是避免用全局对象存储大量的数据。

2 闭包(closure)

我们把【代码 1】稍加改动便可以得到一个闭包导致内存泄漏的版本:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>memory-leak</title>
  </head>
  <body>
    <p>push date for <button>0</button> times</p>
    <p>add Date: <button>add date</button></p>
    <p>clear: <button>clear</button></p>
    <script>
      const pushDate = document.querySelector(".push-date");
      const dateCount = document.querySelector(".count-date");
      let ary = [];
      const wrap = () => {
        const dateAry = Array(3_000).map(() => new Date());
        const inner = () => {
          return dateAry;
        };
        return inner;
      };
      // 【写入 date】
      pushDate.addEventListener("click", function add() {
        ary.push(wrap());
        dateCount.innerHTML = `${ary.length}`;
      });
      const clear = document.querySelector(".clear");
      // 【回收内存】
      clear.addEventListener("click", function clear() {
        ary = [];
        dateCount.innerHTML = `${ary.length}`;
      });
</script>
  </body>
</html>
ログイン後にコピー

代码 3 闭包导致内存泄漏

将上述代码加载到 chrome 中,并用 timeline 的方式录制一个 Snapshot,得到的结果如图 15 所示:

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

图 15 闭包的录制结果

我们选中 index = 2 的心跳,可以看到 Constructor 里面出现了一个 "(closure)",我们展开这个 closure,可以看到里面的 "inner()",inner() 后面的 "()" 表示 inner 是一个函数,这时候你可能会问:“图中的 Constructor 的 Retained Size 大小都差不多,为什么你要选 (closure)?”,正是因为没有明显占比较高的 Retained Size 我们才随便选一个调查,后面你会发现不管你选了哪一个最后的调查链路都是殊途同归的。

我们在下面的 Retainers 中看下 inner() 的持有细节:从下面的 Retainers 中可以看出 inner() 这个 closure 是某个 Array 的第 2 项(index 从 0 开始),而这个数组的持有者是 system/Context(即全局) 中的 ary,通过观察可以看到 ary 的持有大小(Retained Size)是 961KB 大约等于 192KB 的 5 倍,5 即是我们点击“add date”按钮的次数,而下面的 5 个 "previous in system/Context" 每个大小都是 192KB,而它们最终都是被某个 inner() 闭包持有,至此我们便可以得出结论:全局中有一个 ary 数组,它的主要内存是被 inner() 填充的,通过蓝色的 index.html:xx 处的代码入口定位到代码所在地看一下一切就都了然了,原来是 inner() 闭包内部持有了一个大对象,并且所有的 inner() 闭包及其持有的大对象都被 ary 对象持有,而 ary 对象是全局的不会被回收,导致了内存泄漏(如果这种行为不符合预期的话)。返回去,如果这个时候你选择上面提到的 system/Context 构造器,你会看到(见图 16,熟悉吧):

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

图 16 system/Context

也就是你选择的 system/Context 其实是 inner() 闭包的上下文对象(context),而此上下文持有了 192KB 内存,通过蓝色的 index.html:xx 又可以定位到问题代码了。如果你像图 17 一样选择了 Date 构造器进行查看的话也可以最终定位到问题,此处将分析过程留给读者自己进行:

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

图 17 选中 Date 构造器

3 Detached DOM

我们先看一下下面的代码,并用 chrome 载入它:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>memory-leak</title>
  </head>
  <body>
    <p>add Date: <button>add date</button></p>
    <p>delete button: <button>del</button></p>
    <script>
      const addDate = document.querySelector(".push-date");
      const del = document.querySelector(".del");
      function add() {}
      addDate.addEventListener("click", add);
      del.addEventListener("click", function del() {
        addDate.remove();
      });
</script>
  </body>
</html>
ログイン後にコピー

代码 4 Detached Dom

然后我们采用 Heap Snapshot 的方式将点击“del”按钮前后的两个 snapshot 录制下来,得到的结果如图 6 所示。我们选用和 snapshot 1 对比的方式并在 snapshot 2 的过滤器中输入 "detached"。我们观察得到的筛选结果的 "Delta" 列,其中不为 0 的列如下:

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

要解释上述表格需要先介绍一个知识点:DOM 对象被回收需要同时满足两个条件,1、DOM 在 DOM 树中被删掉;2、DOM 没有被 JS 对象引用。其中第二点还是比较容易被忽视的。正如上面的例子所示,Detached HTMLButtonElement +1 代表有一个 button DOM 被从组件树中删掉了,但是仍有 JS 引用之(我们不考虑有意为之的情况)。

相似的,Detached EventListener 也是因为 DOM 被删掉了,但是事件没有解绑,于是 Detached 了,解决方案也很简单:及时解绑事件即可。

于是解决的方法就很简单了:参见代码 5,回掉函数 del 在执行完毕时临时变量会被回收,于是两个条件就都同时满足了,DOM 对象就会被回收掉,事件解绑了,Detached EventListener 也就没有了。值得注意的是 table 元素,如果一个 td 元素发生了 detached,则由于其自身引用了自己所在的 table,于是整个 table 就也不会被回收了。

<script>
      const del = document.querySelector(".del");
      function add() {}
      document.querySelector(".push-date").addEventListener("click", add);
      del.addEventListener("click", function del() {
        document.querySelector(".push-date").removeEventListener("click", add);
        document.querySelector(".push-date").remove();
      });
</script>
ログイン後にコピー

代码 5 Detached DOM 的解决方法

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

图 18 Detached DOM 的 Snapshot

Performance monitor 工具

DOM/event listener 泄漏在编写轮播图、弹窗、toast 提示这种工具的时候还是很容易出现的,chrome 的 devtools 中有一个 Performance monitor 工具可以用来帮助我们调查内存中是否有 DOM/event listener 泄漏。首先看一下代码 6:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>memory-leak</title>
  </head>
  <body>
    <p>add Date: <button>add date</button></p>
    <div></div>
    <script>
      const btnList = document.querySelector(".btn-list");
      const addDate = document.querySelector(".push-date");
      addDate.addEventListener("click", function del() {
        const btn = document.createElement("button");
        btn.innerHTML = "a btn";
        btnList.appendChild(btn);
      });
</script>
  </body>
</html>
ログイン後にコピー

代码 6 不断增加 DOM NODE

按照我们图 19 的方式打开 Performance monitor 面版:

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

图 19 打开 Performance monitor 工具

DOM Nodes 右侧的数量是当前内存中的所有 DOM 节点的数量,包括当前 document 中存在的和 detached 的以及计算过程中临时创建的,每当我们点击一次“add date”按钮,并手动触发 GC 之后 DOM Nodes 的数量就 + 2,这是因为我们向 document 中增加了一个 button 节点和一个 button 的文字节点,就像图 20 中所示。如果你写的 toast 组件在临时插入到 document 并过一会儿执行了 remove 之后处于了 detached 状态的话,Performance monitor 面版中的 DOM Nodes 数量就会不断增加,结合 snapshot 工具你便可以定位到问题所在了。值得一提的是,有的第三方的库的 toast 便存在这个问题,不知道你被坑过没有。

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

图 20 不断增加的 DOM Nodes

4 console

这一点可能有人不会留意到,控制台打印的内容是需要始终保持引用的存在的,这一点也是值得注意的,因为打印过多过大对象的话也是会造成内存泄漏的,如图 21 所示(配合代码 7)。解决方法便是不要肆意打印对象到控制台中,只打印必要的信息出来。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>memory-leak</title>
  </head>
  <body>
    <p>add Date: <button>add date</button></p>
    <div></div>
    <script>
      const addDate = document.querySelector(".push-date");
      addDate.addEventListener("click", function del() {
        const tmp = Array(3_000)
          .fill()
          .map(() => new Date());
        console.info(tmp);
      });
</script>
  </body>
</html>
ログイン後にコピー

代码 7 console 导致内存泄漏

JS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?

图 21 console 导致的内存泄漏

三 总结

本文用了几个简单的小例子介绍了内存泄漏出现的时机、寻找泄漏点的方法并将各种方法的优缺点进行了对比,总结了避免出现内存泄漏的注意点。希望能对读者有所帮助。文中如果有本人理解错误或书写错误的地方欢迎留言指正。

参考

https://commandlinefanatic.com/cgi-bin/showarticle.cgi?article=art038
https://developer.chrome.com/docs/devtools/memory-problems/
https://www.bitdegree.org/learn/chrome-memory-tab
ログイン後にコピー

以上がJS のメモリ リークが発生した場合はどうすればよいですか? JSのメモリリークを回避するにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

JavaScriptの文字列文字を交換します JavaScriptの文字列文字を交換します Mar 11, 2025 am 12:07 AM

JavaScript文字列置換法とFAQの詳細な説明 この記事では、javaScriptの文字列文字を置き換える2つの方法について説明します:内部JavaScriptコードとWebページの内部HTML。 JavaScriptコード内の文字列を交換します 最も直接的な方法は、置換()メソッドを使用することです。 str = str.replace( "find"、 "置換"); この方法は、最初の一致のみを置き換えます。すべての一致を置き換えるには、正規表現を使用して、グローバルフラグGを追加します。 str = str.replace(/fi

独自のAjax Webアプリケーションを構築します 独自のAjax Webアプリケーションを構築します Mar 09, 2025 am 12:11 AM

それで、あなたはここで、Ajaxと呼ばれるこのことについてすべてを学ぶ準備ができています。しかし、それは正確には何ですか? Ajaxという用語は、動的でインタラクティブなWebコンテンツを作成するために使用されるテクノロジーのゆるいグループ化を指します。 Ajaxという用語は、もともとJesse Jによって造られました

10 jQueryの楽しみとゲームプラグイン 10 jQueryの楽しみとゲームプラグイン Mar 08, 2025 am 12:42 AM

10の楽しいjQueryゲームプラグインして、あなたのウェブサイトをより魅力的にし、ユーザーの粘着性を高めます! Flashは依然としてカジュアルなWebゲームを開発するのに最適なソフトウェアですが、jQueryは驚くべき効果を生み出すこともできます。また、純粋なアクションフラッシュゲームに匹敵するものではありませんが、場合によってはブラウザで予期せぬ楽しみもできます。 jquery tic toeゲーム ゲームプログラミングの「Hello World」には、JQueryバージョンがあります。 ソースコード jQueryクレイジーワードコンポジションゲーム これは空白のゲームであり、単語の文脈を知らないために奇妙な結果を生み出すことができます。 ソースコード jquery鉱山の掃引ゲーム

独自のJavaScriptライブラリを作成および公開するにはどうすればよいですか? 独自のJavaScriptライブラリを作成および公開するにはどうすればよいですか? Mar 18, 2025 pm 03:12 PM

記事では、JavaScriptライブラリの作成、公開、および維持について説明し、計画、開発、テスト、ドキュメント、およびプロモーション戦略に焦点を当てています。

jQuery Parallaxチュートリアル - アニメーションヘッダーの背景 jQuery Parallaxチュートリアル - アニメーションヘッダーの背景 Mar 08, 2025 am 12:39 AM

このチュートリアルでは、jQueryを使用して魅惑的な視差の背景効果を作成する方法を示しています。 見事な視覚的な深さを作成するレイヤー画像を備えたヘッダーバナーを構築します。 更新されたプラグインは、jQuery 1.6.4以降で動作します。 ダウンロードしてください

ブラウザでのパフォーマンスのためにJavaScriptコードを最適化するにはどうすればよいですか? ブラウザでのパフォーマンスのためにJavaScriptコードを最適化するにはどうすればよいですか? Mar 18, 2025 pm 03:14 PM

この記事では、ブラウザでJavaScriptのパフォーマンスを最適化するための戦略について説明し、実行時間の短縮、ページの負荷速度への影響を最小限に抑えることに焦点を当てています。

Matter.jsを始めましょう:はじめに Matter.jsを始めましょう:はじめに Mar 08, 2025 am 12:53 AM

Matter.jsは、JavaScriptで書かれた2D Rigid Body Physics Engineです。このライブラリは、ブラウザで2D物理学を簡単にシミュレートするのに役立ちます。剛体を作成し、質量、面積、密度などの物理的特性を割り当てる機能など、多くの機能を提供します。また、重力摩擦など、さまざまな種類の衝突や力をシミュレートすることもできます。 Matter.jsは、すべての主流ブラウザをサポートしています。さらに、タッチを検出し、応答性が高いため、モバイルデバイスに適しています。これらの機能はすべて、物理ベースの2Dゲームまたはシミュレーションを簡単に作成できるため、エンジンの使用方法を学ぶために時間をかける価値があります。このチュートリアルでは、このライブラリのインストールや使用法を含むこのライブラリの基本を取り上げ、

jqueryとajaxを使用した自動更新Divコンテンツ jqueryとajaxを使用した自動更新Divコンテンツ Mar 08, 2025 am 12:58 AM

この記事では、JQueryとAjaxを使用して5秒ごとにDivのコンテンツを自動的に更新する方法を示しています。 この例は、RSSフィードからの最新のブログ投稿と、最後の更新タイムスタンプを取得して表示します。 読み込み画像はオプションです

See all articles