🙆

【初級者脱却】【一問一答】javaScriptのオブジェクト・配列について

2025/02/25に公開

前書

若手エンジニアがつまづきやすいであろう、javaScriptのオブジェクトや配列の挙動について、読むだけよりも実際に自分で考えて答えを出す方が理解が進むかと思うので、一問一答形式で書いていきます。
console.log()で何が出力されるかを考えてみてください。

問題集

  • Question
const obj1 = {
  key1: "val1",
  key2: "val2"
};
const obj2 = {
  key1: "val1",
  key2: "val2"
};
console.log(obj1 === obj2);
Answer
false
// obj1とobj2は中身は一緒でも別の場所にある箱を見ているので、違うものです。
  • Question
const obj1 = {
  key1: "val1",
  key2: "val2"
};
const obj2 = obj1
console.log(obj1 === obj2);
Answer
true
// obj2はobj1を見に行くので、同じものになります。
  • Question
const arr1 = [
  "val1",
  "val2"
];
const arr2 = [
  "val1",
  "val2"
];
const arr3 = arr1;
console.log(arr1 === arr2);
console.log(arr1 === arr3);
Answer
false
true
// 配列でも同じようになります。
  • Question
const obj1 = {
  key1: "val1",
  key2: "val2"
};
const obj2 = obj1;
obj1.key1 = "val1edit";
console.log(obj1);
console.log(obj2);
Answer
// obj1
{
  key1: "val1edit",
  key2: "val2"
}
// obj2
{
  key1: "val1edit",
  key2: "val2"
}
// obj2はobj1と同じものなので、obj1の中身が変更された場合、obj2をconsole.logで出力するとobj1と同様値は変更されれています。
  • Question
const arr1 = [
  "val1",
  "val2"
];
const arr2 = arr1;
arr2[1] = "val2edit";
arr1[2] = "val3";
console.log(arr1);
console.log(arr2);
Answer
// arr1
["val1", "val2edit", "val3"]
// arr2
["val1", "val2edit", "val3"]
// 配列でも同様の動きです。
  • Question
const obj1 = {
  key1: "val1",
  key2: "val2",
  key3: {
      innerKey1: "innerVal11",
      innerKey2: "innerVal12"
  }
};
const obj2 = obj1;
obj1.key3.innerKey2 = "innerVal2edit";
console.log(obj1);
console.log(obj2);
Answer
// obj1
{
  key1: "val1",
  key2: "val2",
  key3: {
    innerKey1: "innerVal11",
    innerKey2: "innerVal2edit"
  }
}
// obj2
{
  key1: "val1",
  key2: "val2",
  key3: {
    innerKey1: "innerVal11",
    innerKey2: "innerVal2edit"
  }
}
// 階層が深くても動きは変わりません。
  • Question
const obj1 = {
  key1: "val1",
  key2: "val2"
};
const obj2 = { ...obj1 };
console.log(obj1 === obj2);
Answer
false
// スプレッド構文はシャローコピーを作るため、obj1とobj2は別ものです。
  • Question
const obj1 = {
  key1: "val1",
  key2: "val2"
};
const obj2 = { ...obj1 };
obj1.key2 = "val2edit";
console.log(obj1);
console.log(obj2);
Answer
// obj1
{
  key1: "val1",
  key2: "val2edit"
}
// obj2
{
  key1: "val1",
  key2: "val2"
}
// obj1とobj2は別ものなので、obj1.key2を変えてもobj2.key2には影響はありません。
  • Question
const obj1 = {
  key1: "val1",
  key2: "val2",
  key3: {
      innerKey1: "innerVal1",
      innerKey2: "innerVal2"
  }
};
const obj2 = { ...obj1 };
obj1.key3.innerKey2 = "innerVal2edit";
console.log(obj1);
console.log(obj2);
Answer
// obj1
{
  key1: "val1",
  key2: "val2",
  key3: {
    innerKey1: "innerVal1",
    innerKey2: "innerVal2edit"
  }
}
// obj2
{
  key1: "val1",
  key2: "val2",
  key3: {
    innerKey1: "innerVal1",
    innerKey2: "innerVal2edit"
  }
}
// スプレッド構文はあくまでもシャローコピーのため、浅い階層しか複製されません。深い階層のobj2.key3.innerKey2はobj1.key3.innerKey2を参照しています。
  • Question
const obj1 = {
  key1: "val1",
  key2: "val2"
};
const obj2 = structuredClone(obj1);
obj1.key2 = "val2edit";
console.log(obj1);
console.log(obj2);
Answer
// obj1
{
  key1: "val1",
  key2: "val2edit"
}
// obj2
{
  key1: "val1",
  key2: "val2"
}
// structuredClone()はディープコピーを作成します。obj1とobj2は完全に別ものです。
// lodashのcloneDeep()を使っても同じですね。
  • Question
const obj1 = {
  key1: "val1",
  key2: "val2",
  key3: {
      innerKey1: "innerVal1",
      innerKey2: "innerVal2"
  }
};
const obj2 = structuredClone(obj1);
obj1.key3.innerKey2 = "innerVal2edit";
console.log(obj1);
console.log(obj2);
Answer
// obj1
{
  key1: "val1",
  key2: "val2",
  key3: {
    innerKey1: "innerVal1",
    innerKey2: "innerVal2edit"
  }
}
// obj2
{
  key1: "val1",
  key2: "val2",
  key3: {
    innerKey1: "innerVal1",
    innerKey2: "innerVal2"
  }
}
// ディープコピーすれば深い階層まで複製されるので、obj1.key3.innerKey2を変えてもobj2.key3.innerKey2には影響ありません。
  • Question
const obj1 = {
  key1: "val1",
  key2: "val2",
  key3: {
      innerKey1: "innerVal1",
      innerKey2: "innerVal2"
  }
};

const obj2 = obj1;
delete obj1.key2;
delete obj2.key3.innerKey2;
console.log(obj1);
console.log(obj2);
Answer
// obj1
{
  key1: "val1",
  key3: {
    innerKey1: "innerVal1"
  }
}
// obj2
{
  key1: "val1",
  key3: {
    innerKey1: "innerVal1"
  }
}
// deleteを使う事で、オブジェクトの要素をkey名を指定して削除することが出来ます。
  • Question
const obj1 = {
  key1: "val1",
  key2: "val2"
};
Object.freeze(obj1);
const obj2 = obj1;
obj1.key1 = "val1edit";
obj2.key3 = "val3";
delete obj1.key2;
console.log(obj1);
console.log(obj2);
Answer
{
  key1: "val1",
  key2: "val2"
}
{
  key1: "val1",
  key2: "val2"
}
// オブジェクトはconstで定義しても要素の変更、追加、削除が可能ですが、Object.freeze()を使うと、オブジェクトを不変に出来ます。
// ここではobj1をfreezeしていますが、obj1とobj2は同じものなので、どちらに対して変更を加えようとしても変更できません。
  • Question
const obj1 = {
  key1: "val1",
  key2: "val2",
  key3: {
      innerKey1: "innerVal1",
      innerKey2: "innerVal2"
  }
};
Object.freeze(obj1);
const obj2 = obj1;
obj1.key3.innerKey2 = "innerVal2edit";
console.log(obj1);
console.log(obj2);
Answer
// obj1
{
  key1: "val1",
  key2: "val2",
  key3: {
    innerKey1: "innerVal1",
    innerKey2: "innerVal2edit"
  }
}
// obj2
{
  key1: "val1",
  key2: "val2",
  key3: {
    innerKey1: "innerVal1",
    innerKey2: "innerVal2edit"
  }
}
// Object.freeze()で不変に出来るのは第一階層のみです。
// 深い階層のオブジェクトまで不変にしたい場合、ここではObject.freeze(obj1.key3)をする必要があります。

Discussion