[[オブジェクト (JavaScript)|オブジェクト]]のクエストとしてはラストです。[[Object.assign (JavaScript)|Object.assign]]や[[スプレッド構文 (JavaScript)|スプレッド構文]]、[[オブジェクト (JavaScript)|オブジェクト]]の複製方法を学びます。 ## Reference - [オブジェクト · JavaScript Primer \#jsprimer](https://jsprimer.net/basic/object/) ## Lesson `Object.assign(targetObj, sourceObj)` で `targetObj` に対して `sourceObj` の[[プロパティ (JavaScript)|プロパティ]]を追加/上書きします。このとき **`targetObj` には破壊的変更** が加わります。 ```js const targetObj = { id: 1, name: "タツヲ" }; const sourceObj = { name: "みみぞう", favorite: "わたあめ" }; const dstObj = Object.assign(targetObj, sourceObj); // targetObj = { id: 1, name: "みみぞう", favorite: "わたあめ" } // sourceObj = { name: "みみぞう", favorite: "わたあめ" } // dstObj = { id: 1, name: "みみぞう", favorite: "わたあめ" } ``` [[📗TDQ-008 関数]]でも少し登場した[[スプレッド構文 (JavaScript)|スプレッド構文]]は、値を分解して関数の引数に渡す以外に、[[オブジェクト (JavaScript)|オブジェクト]]の[[プロパティ (JavaScript)|プロパティ]]として指定することもできます。 ```js const masaharuLover = { favorite: "マサハル" }; const tatsuwo = { id: 1, name: "タツヲ", ...masaharuLover }; // tatsuwo = { id: 1, name: "タツヲ", favorite: "マサハル" } ``` ## Mission 1 #😁EASY 以下のテストが失敗する理由を説明してください。 ```js import { expect } from "jsr:@std/expect"; Deno.test("test", () => { const obj1 = { id: 1, name: "Ichi" }; const obj2 = Object.assign(obj1, { name: "みみぞう" }); expect(obj1.name).toBe("Ichi"); expect(obj2.name).toBe("みみぞう"); }); ``` %% 解答例 [[Object.assign (JavaScript)|Object.assign]]は第1引数の[[オブジェクト (JavaScript)|オブジェクト]]を変更してしまうため、`{ name: "みみぞう" }` によって `obj1.name` が `"みみぞう"` に上書きされてしまったから。 %% ## Mission 2 #🙂NORMAL [[#Mission 1]]のコードがテストに成功するよう、以下の行を変更してください。 **[[Object.assign (JavaScript)|Object.assign]]** を使ってください。 ```js const obj2 = Object.assign(obj1, { name: "みみぞう" }); ``` %% 解答例 ```js const obj2 = Object.assign({}, obj1, { name: "みみぞう" }); ``` %% > [!hint]- Hint 1 > [[Object.assign (JavaScript)|Object.assign]]の引数は可変長であり、第2引数移行はすべてアサインされる[[オブジェクト (JavaScript)|オブジェクト]]として扱われます。 ## Mission 3 #😁EASY [[#Mission 1]]のコードがテストに成功するよう、以下の行を変更してください。 **[[スプレッド構文 (JavaScript)|スプレッド構文]]** を使ってください。 ```js const obj2 = Object.assign(obj1, { name: "みみぞう" }); ``` %% 解答例 ```js const obj2 = { ...obj1, name: "みみぞう" }; ``` %% ## Mission 4 #😵HARD 以下の **テストコード** は通りますが、その内容に **明確な誤り** があります。それを修正してください。 ```js import { expect } from "jsr:@std/expect"; /** * @param {Object} target - クローンの元となるオブジェクト * @param {Object} [source] - クローンにあたり上書きするプロパティのオブジェクト */ function cloneWith(target, source) { return source ? { ...source, ...target } : target; } Deno.test("test", async (t) => { const mimizou = { id: 33, name: "みみぞう" }; await t.step("クローンできる", () => { const actual = cloneWith(mimizou); expect(actual).toEqual({ id: 33, name: "みみぞう" }); expect(actual).toBe(mimizou); }); }); ``` %% 解答例 関連箇所のdiff。 ```js - expect(actual).toBe(mimizou); + expect(actual).not.toBe(mimizou); ``` %% > [!hint]- Hint 1 > クローンで生成された[[オブジェクト (JavaScript)|オブジェクト]]は元の[[オブジェクト (JavaScript)|オブジェクト]]とは異なる参照を持たねばなりません。 > [!hint]- Hint 2 > `expect(...)` のあとに `.not` を追加すると以後のassertionの否定を表現できます。 ## Mission 5 #🙂NORMAL [[#Mission 4]]で修正したテストコードが通過するように `cloneWith` 関数のバグを修正してください。 %% 解答例 関連箇所のdiff。 ```js function cloneWith(target, source) { return source ? { ...source, ...target } : { ...target }; } ``` %% > [!hint]- Hint 1 > 関数の中で新たな[[オブジェクト (JavaScript)|オブジェクト]]を作りなおす必要があります。 ## Mission 6 #😱NIGHTMARE [[#Mission 4]]と[[#Mission 5]]で修正したコードはテストを通りますが、**このコードにはまだ1つ不具合が残っています**。それを修正してください。 %% 解答例 実装のバグは[[スプレッド構文 (JavaScript)|スプレッド構文]]の順番が逆なこと。テストは `source` が指定されたケースの考慮漏れ。 ```js import { expect } from "jsr:@std/expect"; /** * @param {Object} target - クローンの元となるオブジェクト * @param {Object} [source] - クローンにあたり上書きするプロパティのオブジェクト */ function cloneWith(target, source) { return source ? { ...target, ...source } : { ...target }; } Deno.test("test", async (t) => { const mimizou = { id: 33, name: "みみぞう" }; await t.step("クローンできる", () => { const actual = cloneWith(mimizou); expect(actual).toEqual({ id: 33, name: "みみぞう" }); expect(actual).not.toBe(mimizou); }); await t.step("上書きプロパティを指定してCloneできる", () => { const actual = cloneWith(mimizou, { id: 3, favorite: "わたあめ" }); expect(actual).toEqual({ id: 3, name: "みみぞう", favorite: "わたあめ" }); expect(actual).not.toBe(mimizou); }); }); ``` %% > [!hint]- Hint 1 > まずはテストコードを追加しましょう。明らかにテストされていないケースがありますので、まずはそこから。 --- *NEXT* >> [[📗TDQ-015 配列の基礎と検索]]