昨天,我們精心設計了專案的「建築藍圖」——gattProfile
物件。我們學會了如何使用物件 {}
這種「字典」式的結構,來有條理地存放藍牙裝置的各種資訊,每個資訊都有自己專屬的「標籤」(鍵)。
但是,當 Web Bluetooth API 真正開始回報資料時,它通常不會一次只給我們「一個」東西。它會說:「嘿!我找到了一批服務」或「這個服務底下有一整列特徵」。
這時候,光用物件來處理就不太方便了。我們需要一種新的資料結構,專門用來存放一長串有順序的清單。這就是我們今天要精通的主題——陣列 (Array)。
如果說物件是「字典」,那麼陣列就是「購物清單」。今天,我們不僅要學會如何建立這張清單,更要掌握 JavaScript 賦予清單的各種「超能力」(也就是常用方法),讓我們可以高效地對這批資料進行整理、篩選和加工。學會了它,我們才算真正準備好接收來自藍牙裝置的第一批「原始數據」!
核心概念:陣列,就是一個有順序的資料集合。它像一個有很多編號置物櫃的櫃子,你可以把任何東西(字串、數字、布林值、甚至物件)依序放進去。
// 建立一個存放藍牙服務 UUID 的陣列
const serviceUUIDs = [
'0000180d-0000-1000-8000-00805f9b34fb', // 心率服務 (在第 0 個位置)
'0000180f-0000-1000-8000-00805f9b34fb', // 電池服務 (在第 1 個位置)
'0000180a-0000-1000-8000-00805f9b34fb' // 裝置資訊服務 (在第 2 個位置)
];
陣列最大的特點就是「有序」。每個放進去的項目都會被自動分配一個從 0 開始的編號,這個編號就叫做索引 (Index)。
第一個項目的索引是 0
第二個項目的索引是 1
第三個項目的索引是 2
...依此類推
這是程式設計的鐵律,請務必記住:索引永遠從 0 開始!
我們使用 陣列變數[索引]
這種語法來取得特定位置的項目。
// 取得第 1 個項目 (索引為 0)
console.log(serviceUUIDs[0]);
// 輸出: '0000180d-0000-1000-8000-00805f9b34fb'
// 取得第 3 個項目 (索引為 2)
console.log(serviceUUIDs[2]);
// 輸出: '0000180a-0000-1000-8000-00805f9b34fb'
使用 .length
屬性可以告訴你陣列中有多少個項目。
console.log(serviceUUIDs.length); // 輸出: 3
光是能存東西還不夠,JavaScript 給了陣列很多方便的內建「方法」(Method),就像是給了這個置物櫃管理員很多神奇的工具。
.push()
功能:在陣列的最末端加入一個或多個新項目。
專案應用:當我們開始掃描藍牙服務,每發現一個,就可以用 .push()
把它加到一個暫存的清單裡。
const discoveredServices = []; // 先建立一個空的服務清單
console.log("開始掃描...");
// 模擬發現第一個服務
discoveredServices.push({ uuid: '180d', name: '心率服務' });
console.log('目前發現的服務:', discoveredServices);
// 模擬發現第二個服務
discoveredServices.push({ uuid: '180f', name: '電池服務' });
console.log('目前發現的服務:', discoveredServices);
.forEach()
功能:這是 for
迴圈的進化版!它會依序對陣列中的每一個項目執行你指定的函式。
專案應用:這是最常用的方法之一!當我們拿到一個服務列表後,可以用 .forEach()
依序處理每一個服務,把它們的資訊填入我們的 gattProfile
物件中。
const characteristics = [
{ uuid: '2a37', properties: { notify: true, read: false } },
{ uuid: '2a38', properties: { notify: false, read: true } },
{ uuid: '2a29', properties: { notify: false, read: true } }
];
// 語法: array.forEach( item => { ... } );
// 意思: 對 characteristics 陣列中的每一個項目,都執行一次箭頭函式 {...}
// 並且在函式中,用我們自訂的變數名 `char` 來代表當前正在處理的那個項目
characteristics.forEach(char => {
console.log(`正在處理特徵 UUID: ${char.uuid}`);
// 在這裡,我們就可以寫程式碼,把 char 的資訊加到 gattProfile 物件裡
});
.map()
功能:它會回傳一個全新的陣列。新陣列的內容是舊陣列的每一個項目經過你指定的函式「加工」後的結果。
專案應用:假設 API 給了我們一個包含完整特徵物件的陣列,但我們現在只想快速得到一個只包含所有 UUID 的陣列。
const characteristicObjects = [
{ uuid: '2a37', value: 120 },
{ uuid: '2a38', value: 'Chest' },
{ uuid: '2a29', value: 'Google' }
];
// 使用 .map() 進行轉換
const uuidList = characteristicObjects.map(char => {
// return 的值,就是新陣列中對應位置的項目
return char.uuid;
});
console.log(uuidList); // 輸出: ['2a37', '2a38', '2a29']
console.log(characteristicObjects); // 注意:原本的陣列完全沒有改變!
.filter()
和 .find()
.filter()
(篩選器)
功能:回傳一個全新的陣列,新陣列只包含所有符合你指定條件的項目。
專案應用:從所有特徵中,篩選出所有可以讀取 (read: true
) 的特徵。
const allChars = [
{ uuid: '2a37', properties: { read: false, notify: true } }, // 不符合
{ uuid: '2a38', properties: { read: true, notify: false } }, // 符合
{ uuid: '2a29', properties: { read: true, notify: false } } // 符合
];
const readableChars = allChars.filter(char => {
// 條件:回傳 true 的項目會被留下來
return char.properties.read === true;
});
console.log(readableChars); // 會得到一個包含 2a38 和 2a29 這兩個物件的新陣列
.find()
(搜尋器)
功能:只回傳第一個符合你指定條件的項目。如果沒找到,就回傳 undefined
。
專案應用:在眾多特徵中,快速找到 UUID 是 '2a29'
的那個特徵物件。
const targetChar = allChars.find(char => {
return char.uuid === '2a29';
});
console.log(targetChar); // 會得到 { uuid: '2a29', ... } 這個物件
這對新手來說非常重要!
使用陣列 []
的時機:
當你需要一個有順序的清單時。
當你不在乎每個項目的「標籤」,只關心它的內容和順序時。
當你想使用 .forEach()
, .map()
這些方便的陣列方法時。
使用物件 {}
的時機:
當你需要用有意義的標籤 (鍵) 來組織資料時。
當你需要透過「鍵」來快速查找特定資料,而不是透過順序時 (例如我們的 gattProfile
)。
在我們的專案中,我們會兩者並用:Web Bluetooth API 會先給我們陣列形式的原始資料,然後我們會用陣列方法對其進行處理,最後將整理好的結果存入我們設計好的物件 gattProfile
中。
今天我們掌握了處理「一批」資料的強大武器——陣列。我們不僅知道了它是從 0 開始編號的有序清單,還學會了使用 .push()
, .forEach()
, .map()
, .filter()
和 .find()
這些方法,像專家一樣對資料進行新增、遍歷、轉換和篩選。
從明天開始,理論學習將告一段落,我們將第一次呼叫 Web Bluetooth API,讓瀏覽器彈出那個令人興奮的藍牙掃描視窗。
今天的內容就到這邊,感謝你能看到這裡,在這邊祝你早安、午安、晚安,我們明天見。