[EffectiveJavaScript輪読会5]インスタンスの状態は、インスタンスオブジェクトにだけ保存する

GAS

どうも。つじけ(tsujikenzo)です。このシリーズでは2021年8月からスタートしました「ノンプロ研EffectiveJavaScript輪読会」についてお送りします。

前回のおさらい

前回は、「メソッドをプロトタイプに格納しよう」「プライベートデータの格納にはクロージャを使おう」を2本まとめてお届けしました。

[EffectiveJavaScript輪読会5]メソッドをプロトタイプに格納しよう
どうも。つじけ(tsujikenzo)です。このシリーズでは2021年8月からスタートしました「ノンプロ研EffectiveJavaScript輪読会」についてお送りします。前回のおさらい前回は、「newに依存しないコンス...

今回は、「インスタンスの状態は、インスタンスオブジェクトにだけ保存する」「thisの暗黙的な結合を理解しよう」 を2本まとめてお届けします。

テキスト第4章「オブジェクトとプロトタイプ」の項目36、項目37に対応しています。

今日のアジェンダ

  • インスタンスの状態は、インスタンスオブジェクトにだけ保存する
  • thisの暗黙的な結合を理解しよう

インスタンスの状態は、インスタンスオブジェクトにだけ保存する

ツリーデータ構造を実装するクラスは、それぞれのノードが、子ノードの配列を含むことになります。

子ノードの配列(インスタンスの状態)を、プロトタイプに書いてしまうと、構造が壊れてしまいます。

function myFunction5_36_01() {


  function Tree(x) {
    this.value = x;
  }

  Tree.prototype = {
    children: [],//インスタンスの状態
    addChild: function (x) {
      this.children.push(x)
    }

  }

  const left = new Tree(2);
  left.addChild(1);
  left.addChild(3);

  const right = new Tree(6);
  right.addChild(5);
  right.addChild(7);

  const top = new Tree(4);
  top.addChild(left);
  top.addChild(right);

  console.log(top.children); //	[ 1, 3, 5, 7, { value: 2 }, { value: 6 } ]

}

なので、このように、インスタンスの状態は、インスタンスオブジェクトだけに保存するようにします。

コンストラクタで、インスタンス変数に格納します。

function myFunction5_36_02() {


  class Tree {
    constructor(x) {
      this.value = x;
      this.children = [];//インスタンスの状態
    }

    addChild(x) {
      this.children.push(x)
    }

  }

  const left = new Tree(2);
  left.addChild(1);
  left.addChild(3);

  const right = new Tree(6);
  right.addChild(5);
  right.addChild(7);

  const top = new Tree(4);
  top.addChild(left);
  top.addChild(right);

  console.log(left.children); //	[ 1, 3 ]
  console.log(right.children); //	[ 5, 7 ]
  console.log(top.children); //		[ { value: 2, children: [ 1, 3 ] }, { value: 6, children: [ 5, 7 ] } ]

}

書籍では、以下の3点をまとめていました。

あたまにいれておくと、あとで便利だなと思ったので、書き残しておきます。

  • 変化するデータを共有すると問題が多い
  • プロトタイプは、そのすべてのインスタンスで共有される
  • インスタンスごとに変化する状態は、インスタンスオブジェクトに保存する

thisの暗黙的な結合を理解しよう

暗黙的なthisには気をつけようという話は、なんどもしてきました。

use strictモードで、引数を展開させたコールバック関数を、アロー関数で書こうというのが、現時点の最適解です。

function myFunction05_37_01() {
'use strict'

  class CSVreader {
    constructor(separators) {
      this.separators = separators || [","];
      const regexpArray = this.separators.map(sep => "\\" + sep[0]).join("|");
      this.regexp = new RegExp(regexpArray);
    }

    read(str) {
      const lines = str.trim().split(/\n/);
      return lines.map(line => line.split(this.regexp));
    }

  }

  const reader = new CSVreader();
  console.log(reader.read("a,b,c\nd,e,f\n")); //[ [ 'a', 'b', 'c' ], [ 'd', 'e', 'f' ] ]

  const reader2 = new CSVreader(["_"]);
  console.log(reader2.read("a_b_c\nd_e_f\n")); //[ [ 'a', 'b', 'c' ], [ 'd', 'e', 'f' ] ]

}

まとめ

以上で、「インスタンスの状態は、インスタンスオブジェクトにだけ保存する」「thisの暗黙的な結合を理解しよう」を2本まとめてお届けしました。

次回は、「スーパークラスのコンストラクタは、サブクラスのコンストラクタから呼び出す」「スーパークラスのプロパティ名は、決して再利用しない」 を2本まとめてお届けします。

このシリーズの目次

タイトルとURLをコピーしました