【JavaScript】ライブオブジェクトとは何者なのか

JavaScriptを勉強するなかで『ライブオブジェクト』という特殊な挙動をする奴に出会ったので、その特徴をまとめます。

コードが意図したのと違う動きをするときは、ライブオブジェクトの仕業かもしれません。

ライブオブジェクトとは

JavaScriptのオブジェクト(ここでいうオブジェクトは『モノ』みたいな意味です)は、2種類に分けることができます。スタティックオブジェクトと、ライブオブジェクトです。

対義語が『スタティック(静的、つまり動かない)なオブジェクト』であることからわかるように、ライブオブジェクトはまるで「生きているかのように動く」のが特徴です。

なにをいっているのか分からないとおもうので、以下のコードをみてください。

<ul id="target">
    <li></li>
    <li></li>
    <li></li>
</ul>
			
<script>
    const target = document.getElementById('target');
    const lists = document.getElementsByTagName('li');

    console.log(lists.length);
    //1回目のコンソール表示

    const newLi = document.createElement('li');
    target.appendChild(newLi);

    console.log(lists.length);
    //2回目のコンソール表示
</script>   

いちおう説明すると
①ulタグ内にあるliの数を取得してlistsという定数に代入
②listsを表示(1回目)
③JavaScriptで新しくliをつくり、ulタグに追加
④listsを表示(2回目)
という流れ。

lists.lengthはliの数なので、1回目のconsole.logでは当然3を返します。
では、2回目のconsole.logではなにが返ると思いますか?

注目してほしいのは、listsの代入の位置。
liの数を取得したのははじめだけなので3が返るはず...と思いきや、2回目のconsole.logは4を返します。

つまり、指示していないのに勝手に更新されている、ということ。

これがライブオブジェクトの特徴です。気持ちわるい...。

ライブオブジェクトの挙動について

ライブオブジェクトがなぜこんな挙動をするのかというと、コイツが常にDOMツリーを参照しているから。

つまり、DOMツリーが修正されると(さきほどの例でいえば③の操作)、その修正は同時にライブオブジェクトにも反映される、ということになります。

ですので、以下のコードは無限ループになってしまいます。

<ul id="target">
    <li></li>
    <li></li>
    <li></li>
</ul>
			
<script>
    const target = document.getElementById('target');
    const lists = document.getElementsByTag('li');

    for(let i = 0; i < lists.length; i++){
        const newLi = document.createElement('li');
        target.appendChild(newLi);
    }
</script>   

ulタグ内のliの数だけ新しくliタグを追加する(つまりこの場合、liを3つ追加する)という動作を期待したコードです。

ところが、for文に指定したlistsはライブオブジェクト。liを追加した時点でlists.lengthも増えるので無限ループになる、というわけです。

これを避けるには、以下のようにします。

<script>
    const target = document.getElementById('target');
    const lists = document.getElementsByTag('li');

    for(let i = 0; length = lists.length; i < length; i++){
        const newLi = document.createElement('li');
        target.appendChild(newLi);
    }
</script>

おなじことのように見えますが、ループ処理に入るまえにlists.lengthを取得しておき、それを条件につかうことで解決します。

ライブオブジェクトを扱うときの注意点

嫌な動きをするライブオブジェクトですが、使うときは3つのアプローチがあるようです。

①ライブオブジェクトの変化に影響されない書き方にする(上の例)
②ライブオブジェクトが変化することを期待した処理にする
③ライブオブジェクトが変化するまえに処理を終える

ネイティブのJavaScriptによるDOM操作では、ライブなオブジェクトを扱うケースが多いと思います。

いじっているオブジェクトがライブなのか、はたまたスタティックなのか。思わぬところでハマらないように意識しておきたいですね。

この記事が気に入ったらサポートをしてみませんか?