---
title: JS直播班練習 - 物件與陣列設計
tags: 陣列, 物件,
description:
---
# 物件與陣列設計
## 本章節我們要學什麼?
前面幾週練習了字串與數字,這次要來學習「物件與陣列」。在 Javascript 中,**陣列是一種具有特殊方法與屬性的物件**,可以把陣列視作「特別用於存儲有序列狀的資料」的資料型態。
* 物件:一個物體,由鍵與值構成與描述。
* 陣列:一個清單,由元素和索引構成。
參考文件:[MDN陣列](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array)、[MDN物件](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/)


## 題目一:創建陣列與物件
請分別創建一個陣列與物件用於存儲以下資訊並在瀏覽器的 Console 中打印出來:
* **待買備忘錄 (用陣列表示)**
* 牛奶
* 酪梨
* 咖啡粉
* 蘋果麵包
* **用戶的資訊 (用物件表示)**
* 年紀: 20
* 名稱: 小明
* 生理性別: 男
* 興趣: 前端開發、讀書、跑步
* 喜歡的食物: 滷肉飯
* 吃素: 否
``` =JavaScrip
// 附註:同學可嘗試放入任何自訂內容,著重在練習語法操作。
// Todo 待修改
const toBuyList = []
const userInfo = {}
console.log(toBuyList)
console.log(userInfo)
// Output 輸出
["牛奶", "酪梨", "咖啡粉", "蘋果麵包"]
{
age: 20,
name: "小明",
sex: "男",
hobby: ["前端開發", "讀書", "跑步"],
favoriteFood: "滷肉飯",
isVegetarian: false
}
```
**解答:**
```javascript=
const toBuyList = ['牛奶', '酪梨', '咖啡粉', '蘋果麵包'];
const userInfo = {
age: 20,
name: '小明',
sex: 'male',
hobby: ['前端開發', '讀書', '跑步'],
favoriteFood: '滷肉飯',
isVegetarian: false,
};
console.log(toBuyList);
console.log(userInfo);
```
## 題目二:獲取陣列與物件長度
接續第一題,請修改以下程式使其打印出陣列或物件的長度。
參考文件:[MDN Array.length](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/length)、[MDN Object Keys](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Object/keys)
``` =JavaScrip
// Todo 待修改
console.log(toBuyList)
console.log(userInfo)
// Output 輸出
4
6
```
**解答:**
```javascript=
console.log(toBuyList.length);
console.log(Object.keys(userInfo).length);
```
## 題目三:獲取陣列或物件內容
接續第一題,請將備忘錄與用戶資訊的**第一筆與最後一筆資料**給打印出來,如果是物件,須將 key 與 value 用以下範例格式打印出來。
參考文件:[MDN Object Keys](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Object/keys)、[MDN Object Values](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values)
``` =JavaScrip
// Todo 待修改
console.log(toBuyList)
console.log(userInfo)
// Output 輸出
"牛奶","蘋果麵包"
"age: 20 / isVegetarian: false"
```
**解答:**
```javascript=
console.log(`toBuyList的第一筆資料'${toBuyList[0]}',最後一筆資料'${toBuyList[toBuyList.length - 1]}'`);
// 取得物件的第一個屬性
const firstAttribute = Object.keys(userInfo)[0];
// 取得物件的第一個屬性的值
const firstAttributeValue = userInfo[firstAttribute];
// 取得物件的倒數第一個屬性
const lastAttribute = Object.keys(userInfo)[Object.keys(userInfo).length - 1];
// 取得物件的倒數第一個屬性的值
const lastAttributeValue = userInfo[lastAttribute];
console.log(`userInfo的第一筆資料'${firstAttribute} : ${firstAttributeValue}',最後一筆資料'${lastAttribute} : ${lastAttributeValue}'`);
```
## 題目四:複製陣列或物件(淺拷貝)
:::info
如果還沒有概念如何操作,可參考以下文檔。
* [ES6 展開語法:Spread syntax (...)](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/Spread_syntax)
* [物件複製:Object.assign()](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
:::
JavaScript 在創建變數、賦值後除了基本型態外都是以傳址(call by refence)來去記載內容位置,雖然可以節省記憶體使用但也會造成「改 A 更動到 B」的問題。
參考資料:[Pass by value, or Pass by reference?](https://ithelp.ithome.com.tw/articles/10209104)
接續第一題,請複製一份相同的內容(使用淺拷貝的方式)到全新的陣列、物件之中。
``` =JavaScrip
// Todo 待修改
const newToBuyList = []
const newUserInfo = {}
console.log(newToBuyList)
console.log(newUserInfo)
// Output 輸出
["牛奶", "酪梨", "咖啡粉", "蘋果麵包"]
{
age: 20,
name: "小明",
sex: "男",
hobby: ["前端開發", "讀書", "健身"],
favoriteFood: "滷肉飯",
isVegetarian: false
}
```
**解答:**
```javascript=
// const newToBuyList = Object.assign([], toBuyList);
// const newUserInfo = Object.assign({}, userInfo);
const newToBuyList = [...toBuyList];
const newUserInfo = {...userInfo};
console.log(newToBuyList);
console.log(newUserInfo);
```
**延伸閱讀**
- [Object.assign()](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
<br>
## 題目五:修改陣列與物件(增、刪、改)
:::info
增、刪、改陣列或物件的方式有很多種,如還沒有概念如何操作可以參考以下文章。
* [(Ray) 關於 JavaScript 陣列 20 種操作的方法](https://israynotarray.com/javascript/20190421/1216566123/)
* [(卡斯伯) JavaScript 陣列處理方法](https://www.casper.tw/javascript/2017/06/29/es6-native-array/)
:::
接續第四題,在淺複製完 ToBuyList、UserInfo 後對陣列與物件做以下的更動,並打印出來。
**陣列:**
* 刪除陣列中的 "咖啡粉"
* 新增 "布丁" 為陣列的首筆資料
* 刪除陣列中的最後一筆資料("蘋果麵包")
**物件:**
* 將小明的年齡 +1
* 將小明是否吃素改為 true
* 新增 key 為 "hasDriverLiscense",value 為 true
* 刪除 key "favoriteFood"
``` =JavaScrip
// Todo 待編輯
//
// 請在此對新物件與陣列做更動。
//
console.log(newToBuyList)
console.log(newUserInfo)
// Output 輸出
["布丁", "牛奶", "酪梨"]
{
age: 21,
name: "小明",
sex: "男",
hobby: ["前端開發", "讀書", "健身"],
isVegetarian: true
hasDriverLiscense: true
}
```
**解答:**
```javascript=
// 陣列:
// 刪除陣列中的 “咖啡粉”
newToBuyList.splice(2, 1);
// 新增 “布丁” 為陣列的首筆資料
newToBuyList.unshift('布丁');
// 刪除陣列中的最後一筆資料(“蘋果麵包”)
newToBuyList.pop();
console.log(newToBuyList);
// 物件:
// 將小明的年齡 +1
newUserInfo.age = 21;
// 將小明是否吃素改為 true
newUserInfo.isVegetarian = true;
// 新增 key 為 "hasDriverLiscense",value 為 true
newUserInfo.hasDriverLiscense = true;
// 刪除 key "favoriteFood"
delete newUserInfo.favoriteFood;
console.log(newUserInfo);
```
<br>
## 題目六:複製陣列或物件(深拷貝)
:::info
關鍵字與參考資料:
* [MDN Json.prase()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse)
* [JSON.stringify()](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)
* Array Deep Clone
:::
``` =JavaScrip
// Input 輸入
const nestedNumbers = [[1], [2]];
const numbersCopy = [...nestedNumbers];
numbersCopy[0].push(300);
console.log(nestedNumbers, numbersCopy);
// Output 輸出
// 兩個變數都被更改了,因為他們共享相同的記憶體位址來存儲資料。
[[1, 300], [2]]
[[1, 300], [2]]
```
**(請先參考以上代碼了解問題點)**
在大多情況下使用淺層拷貝就足矣,但當情況稍微複雜一點(嵌套的陣列或物件),在第二層還是會出現共享記憶體位址來存儲資料的問題,造成:「更動 A 陣列也跟著改動到 B 陣列」的問題。
請創造 nestedNumbers 並深層複製出 deepCloneNestedNumbers,使其各自有獨立的記憶體位置存放資料。
``` =JavaScrip
// Todo 待編輯
const nestedNumbers = [[1], [2]];
const deepCloneNestedNumbers = []
deepCloneNestedNumbers[0].push(300)
console.log(nestedNumbers, deepCloneNestedNumbers)
// Output 輸出
[[1], [2]]
[[1, 300], [2]]
```
**解答:**
```javascript=
const nestedNumbers = [[1], [2]];
const deepCloneNestedNumbers = JSON.parse(JSON.stringify(nestedNumbers));
deepCloneNestedNumbers[0].push(300);
console.log(nestedNumbers, deepCloneNestedNumbers);
```
<br>
## 題目七:月有陰晴圓缺🌚🌝
:::success
先喘一口氣,接著挑戰如何使用原生方法處理陣列。通常會建議同學基本了解以下最常用的方法,就足夠應付大多場合:
* [為什麼要學陣列資料處理?](https://courses.hexschool.com/courses/202011131/lectures/42391273)
* [forEach](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)
* [map](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Map)
* [filter](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)
* [reduce](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)
:::
請製作一個函式可以回傳出月亮一周期的過程,並且在"滿月 🌕"時加上"月餅 🥮"字串。
``` =JavaScrip
// Todo 待編輯
function moonCakeTime(planetPhase) {
}
const moonPhase = ["🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘"]
console.log(moonCakeTime(moonPhase))
// Output 輸出
["🌑","🌒","🌓","🌔","🌕 + 🥮","🌖","🌗","🌘"]
```
**解答:**
```javascript=
const moonPhase = ["🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘"]
function moonCakeTime(planetPhase) {
return planetPhase.map(item => item === '🌕' ? item += ' + 🥮' : item += '' )
};
console.log(moonCakeTime(moonPhase));
```
<br>
## 題目八:組合 HTML 標籤
在後續幾週的章節我們會學到如何操作網頁上的 DOM 元素,不過在此只需要先練習組合 HTML 標籤字串即可。
``` =JavaScrip
// Todo 待編輯
const cardData = [{
url: "https://www.hexschool.com/",
content: "六角學院"
}, {
url: "https://www.heptagonschool.com/",
content: "七角學院"
}, {
url: "https://www.Octagonschool.com/",
content: "八角學院"
}]
const cardStructure = `
<li>
<a href="#">Content</a>
<li>
`
const cardHTML = []
console.log(cardHTML)
// Output 輸出
["
<li>
<a href='https://www.hexschool.com/'>六角學院</a>
<li>
","
<li>
<a href='https://www.heptagonschool.com/'>七角學院</a>
<li>
","
<li>
<a href='https://www.Octagonschool.com/'>八角學院</a>
<li>
"]
```
**解答:**
```javascript=
const cardData = [
{
url: "https://www.hexschool.com/",
content: "六角學院"
},
{
url: "https://www.heptagonschool.com/",
content: "七角學院"
},
{
url: "https://www.Octagonschool.com/",
content: "八角學院"
}
];
const cardHTML = [];
cardData.forEach(item => {
const cardStructure = `<li><a href=${item.url}>${item.content}</a><li>`;
cardHTML.push(cardStructure);
});
console.log(cardHTML);
```
<br>
## 題目九:小餐館訂單顯示
小餐館到了中午暴增許多外送訂單,請回傳一個物件,幫助廚師紀錄目前有多少種類的食物與數量要製作。
``` =JavaScrip
// Todo 待編輯
const orders = ["🍔","🍜","🍕","🍜","🍔"];
function countOrders(orders) {
// ...
}
console.log(countOrders(orders))
// Output 輸出
{
🍔 : 2,
🍜 : 2,
🍕 : 1
}
```
**解答:**
```javascript=
const orders = ["🍔", "🍜", "🍕", "🍜", "🍔"];
function countOrders(orders) {
let foodItems = {};
orders.forEach(item => foodItems[item] ? foodItems[item] += 1 : foodItems[item] = 1);
return foodItems;
}
console.log(countOrders(orders));
```
<br>
## 題目十:代辦事項
請同學綜合以上練習,在終端中創造一個代辦事項,分別具有以下功能:
* 新增事項
* 刪除事項
* 切換事項完成狀態
* 渲染事項
預期每筆事項的架構如下:
``` =JavaScrip
{
id: 0, // 自行決定方便的數值,有獨一無二的 ID 最好。
name: "事項名稱",
isCompleted: false,
}
```
``` =JavaScrip
// Todo 待編輯
let tasks = [];
function addTask(task) {
}
function removeTask(targetId) {
}
function toggleTask(targetId) {
}
function renderTasks(tasks) {
}
addTask("A");
addTask("B");
addTask("C");
toggleTask(2);
removeTask(1); // 可以輸入 ID 去刪除事項
renderTasks(tasks);
// 輸出
[
{
"id": 2,
"name": "B",
"isCompleted": true
},
{
"id": 3,
"name": "C",
"isCompleted": false
}
]
```
**解答:**
```javascript=
let tasks = [];
function addTask(task) {
tasks.push({
id: tasks.length + 1,
name: task,
isCompleted: false,
});
};
function removeTask(targetId) {
tasks.forEach((item, index) => {
item.id === targetId ? tasks.splice(index, 1) : ``;
});
};
function toggleTask(targetId) {
tasks.map(item => item.id === targetId ? item.isCompleted = true : item.isCompleted = false);
};
function renderTasks(tasks) {
tasks.forEach(item => console.log(`
第${item.id}項任務
任務名稱:${item.name}
是否完成:${item.isCompleted ? '是' : '否'}
`));
};
addTask("A");
addTask("B");
addTask("C");
toggleTask(2);
removeTask(1);
renderTasks(tasks);
```
<br>