[[オブジェクト (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 配列の基礎と検索]]