iT邦幫忙

2025 iThome 鐵人賽

DAY 8
0
Modern Web

Web Bluetooth API 實戰:30 天打造通用 BLE 偵錯工具系列 第 8

DAY8:資料的集合:陣列與常用方法實戰

  • 分享至 

  • xImage
  •  

昨天,我們精心設計了專案的「建築藍圖」——gattProfile 物件。我們學會了如何使用物件 {} 這種「字典」式的結構,來有條理地存放藍牙裝置的各種資訊,每個資訊都有自己專屬的「標籤」(鍵)。

但是,當 Web Bluetooth API 真正開始回報資料時,它通常不會一次只給我們「一個」東西。它會說:「嘿!我找到了一批服務」或「這個服務底下有一整列特徵」。

這時候,光用物件來處理就不太方便了。我們需要一種新的資料結構,專門用來存放一長串有順序的清單。這就是我們今天要精通的主題——陣列 (Array)

如果說物件是「字典」,那麼陣列就是「購物清單」。今天,我們不僅要學會如何建立這張清單,更要掌握 JavaScript 賦予清單的各種「超能力」(也就是常用方法),讓我們可以高效地對這批資料進行整理、篩選和加工。學會了它,我們才算真正準備好接收來自藍牙裝置的第一批「原始數據」!


1. 什麼是陣列 (Array)?

核心概念:陣列,就是一個有順序的資料集合。它像一個有很多編號置物櫃的櫃子,你可以把任何東西(字串、數字、布林值、甚至物件)依序放進去。

// 建立一個存放藍牙服務 UUID 的陣列
const serviceUUIDs = [
  '0000180d-0000-1000-8000-00805f9b34fb', // 心率服務 (在第 0 個位置)
  '0000180f-0000-1000-8000-00805f9b34fb', // 電池服務 (在第 1 個位置)
  '0000180a-0000-1000-8000-00805f9b34fb'  // 裝置資訊服務 (在第 2 個位置)
];

陣列的「索引 (Index)」- 置物櫃的編號 (新手必看!)

陣列最大的特點就是「有序」。每個放進去的項目都會被自動分配一個從 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

2. 陣列的超能力:常用方法實戰

光是能存東西還不夠,JavaScript 給了陣列很多方便的內建「方法」(Method),就像是給了這個置物櫃管理員很多神奇的工具。

A. 新增項目:.push()

  • 功能:在陣列的最末端加入一個或多個新項目。

  • 專案應用:當我們開始掃描藍牙服務,每發現一個,就可以用 .push() 把它加到一個暫存的清單裡。


const discoveredServices = []; // 先建立一個空的服務清單

console.log("開始掃描...");

// 模擬發現第一個服務
discoveredServices.push({ uuid: '180d', name: '心率服務' });
console.log('目前發現的服務:', discoveredServices);

// 模擬發現第二個服務
discoveredServices.push({ uuid: '180f', name: '電池服務' });
console.log('目前發現的服務:', discoveredServices);

B. 遍歷所有項目:.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 物件裡
});

C. 轉換陣列:.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); // 注意:原本的陣列完全沒有改變!

D. 篩選項目:.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', ... } 這個物件
    

3. 小總結:陣列 vs 物件,何時用哪個?

這對新手來說非常重要!

  • 使用陣列 [] 的時機

    1. 當你需要一個有順序的清單時。

    2. 當你不在乎每個項目的「標籤」,只關心它的內容和順序時。

    3. 當你想使用 .forEach(), .map() 這些方便的陣列方法時。

  • 使用物件 {} 的時機

    1. 當你需要用有意義的標籤 (鍵) 來組織資料時。

    2. 當你需要透過「鍵」來快速查找特定資料,而不是透過順序時 (例如我們的 gattProfile)。

在我們的專案中,我們會兩者並用:Web Bluetooth API 會先給我們陣列形式的原始資料,然後我們會用陣列方法對其進行處理,最後將整理好的結果存入我們設計好的物件 gattProfile 中。


總結與後續

今天我們掌握了處理「一批」資料的強大武器——陣列。我們不僅知道了它是從 0 開始編號的有序清單,還學會了使用 .push(), .forEach(), .map(), .filter().find() 這些方法,像專家一樣對資料進行新增、遍歷、轉換和篩選。
從明天開始,理論學習將告一段落,我們將第一次呼叫 Web Bluetooth API,讓瀏覽器彈出那個令人興奮的藍牙掃描視窗。
今天的內容就到這邊,感謝你能看到這裡,在這邊祝你早安、午安、晚安,我們明天見。


上一篇
Day7:萬物皆物件:設計儲存 GATT Profile 的資料結構
系列文
Web Bluetooth API 實戰:30 天打造通用 BLE 偵錯工具8
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言