🐕

【JavaScript】参照型と値型の違いをclassListプロパティで体感する

に公開

はじめに

要素.classListプロパティの挙動を確かめるため、以下のコードで id 属性が p2 の pタグ を取得し、その値の変化を観察しました。

prac1.html
<body>
    <div id="target1" class="bg-red">
        <p id="p1">
            Hello World 1
        </p>
    </div>

    <div id="target2" class="bg-red">
        <p id="p2">
            Hello World 2
        </p>
    </div>

    <script src="../classList/classList.js"></script>
</body>
classList.js
let container = document.getElementById("target2");

let p2 = document.getElementById("p2");

console.log(p2);

container.classList.remove("bg-red");
container.classList.add("bg-black");

p2.classList.add("text-white");

console.log(p2);

予想では"text-white"が新たなクラスとして加えられると思いましたが、実際の結果は以下のようになりました。

上記のコードを実行した結果

拡大すると…

この記事では、なぜこのような結果になったのかについて、オブジェクトの参照について触れながら解説していきます。

結論

  • pタグが新たな値に上書きされたため
  • そこには、同じオブジェクトの変更による影響がある

console.log()動作

...これらのブラウザーで記録されるのはオブジェクトへの参照です。そのため、 console.log() を呼び出した時点でのオブジェクトの「値」が表示されるのではなく、内容を見るために開いた時点での値が表示されます。
https://developer.mozilla.org/ja/docs/Web/API/console/log_static より引用

console.log()にオブジェクトを渡すと起きること

console.log()にオブジェクトを渡すと、それはオブジェクトの参照(reference)を保持します。参照とは、メモリ上にあるオブジェクトの実際の場所を指し示すポインタのようなものです。

そのため、もし別の場所で同じオブジェクトが更新されると、その参照を介して参照しているオブジェクト自体が変更されるので、console.log()の出力内容もその影響を受けます。

console.log()は、引数として渡されたオブジェクトを、その時点での最新の状態として表示しようとします。しかし、DOMオブジェクトのような動的に変化するオブジェクトの場合、多くのブラウザの開発者ツールでは、リアルタイムで更新された状態を反映するように動作します。

最初のconsole.log(p2)が実行された時点では、p2のクラスはまだ追加されていません。
しかし、コンソールがp2オブジェクトへの参照を保持しているため、その後にp2.classList.add("text-white")が実行されると、コンソールの表示も自動的に更新されます。

参照型の場合
let container = document.getElementById("target2");

let p2 = document.getElementById("p2");

console.log(p2); // ← ここ

container.classList.remove("bg-red");
container.classList.add("bg-black");

p2.classList.add("text-white");

console.log(p2); // ← ここ

最後のconsole.log(p2)が実行される時点では、すでにp2にtext-whiteクラスが追加されているため、class="text-white"を含む状態で出力されます。

結果として、コンソールには常に最新のp2の状態が表示されるため、最初の出力と最後の出力が同じように見えてしまうのです。これは、デバッグの利便性を高めるためのブラウザの機能によるものです。

違いを認識するためには

2つの時点でのオブジェクトの状態を正確に出力するには、プロパティの値を出力する必要があります。
そのために、主に2つの方法があります。

  1. outerHTML を使用する
  2. JSON.stringify()を使用してオブジェクトを文字列に変換する

今回、単純な値の変化を見るために、outerHTMLを使ってみます。

値型の場合
...
console.log(p2.outerHTML); // 変更

container.classList.remove("bg-red");
container.classList.add("bg-black");

p2.classList.add("text-white");

console.log(p2.outerHTML); // 変更

コードを変更した結果

class = "text-white" が追加されたことがわかります。

このことから、参照型を出力した結果、片方の変更がもう片方にも影響を及ぼしたことがわかります。
一方、値型を出力した結果、その瞬間のオブジェクトの状態を文字列としてそれぞれ出力することができました。

まとめ

今回は、オブジェクトの出力結果が違うことは、データ型が参照型か値型かの違いによるものであることを学びました。
参照型は同じメモリアドレスを共有するため、同じそれを使用している場合には他に影響が出ることがあるため、取り扱いには注意する必要があることがわかりました。

最後までお読みいただき、ありがとうございました。

参考URL

https://developer.mozilla.org/ja/docs/Web/API/console/log_static

Discussion