使用 JSON
JavaScript 物件表示法 (JSON) 是一種基於 JavaScript 物件語法表示結構化資料的標準文字格式。它通常用於在 Web 應用程式中傳輸資料(例如,將一些資料從伺服器傳送到客戶端,以便在網頁上顯示,反之亦然)。你經常會遇到它,所以在這篇文章中,我們將向你介紹使用 JavaScript 處理 JSON 所需的一切,包括解析 JSON 以便訪問其中的資料,以及建立 JSON。
不,真的,JSON 是什麼?
JSON 是一種遵循 JavaScript 物件語法的文字資料格式。它將結構化資料表示為字串,這在你想透過網路傳輸資料時非常有用。儘管它與 JavaScript 物件字面量語法非常相似,但它可以獨立於 JavaScript 使用。許多程式設計環境都具備讀取(解析)和生成 JSON 的能力。在 JavaScript 中,解析和生成 JSON 的方法由 JSON 物件提供。
注意:將字串轉換為原生物件稱為反序列化,而將原生物件轉換為字串以便透過網路傳輸稱為序列化。
JSON 字串可以儲存在它自己的檔案中,它基本上只是一個副檔名為 .json 的文字檔案,MIME 型別為 application/json。
JSON 結構
如上所述,JSON 是一個字串,其格式非常類似於 JavaScript 物件字面量格式。以下是一個有效的 JSON 字串,表示一個物件。請注意,它也是一個有效的 JavaScript 物件字面量 — 只是有一些額外的語法限制。
{
"squadName": "Super hero squad",
"homeTown": "Metro City",
"formed": 2016,
"secretBase": "Super tower",
"active": true,
"members": [
{
"name": "Molecule Man",
"age": 29,
"secretIdentity": "Dan Jukes",
"powers": ["Radiation resistance", "Turning tiny", "Radiation blast"]
},
{
"name": "Madame Uppercut",
"age": 39,
"secretIdentity": "Jane Wilson",
"powers": [
"Million tonne punch",
"Damage resistance",
"Superhuman reflexes"
]
},
{
"name": "Eternal Flame",
"age": 1000000,
"secretIdentity": "Unknown",
"powers": [
"Immortality",
"Heat Immunity",
"Inferno",
"Teleportation",
"Interdimensional travel"
]
}
]
}
如果你將此 JSON 作為字串載入到你的 JavaScript 程式中,你可以將其解析為普通物件,然後使用我們在JavaScript 物件基礎知識文章中討論過的相同點/方括號表示法訪問其中的資料。例如:
superHeroes.homeTown;
superHeroes.members[1].powers[2];
- 首先,我們有變數名 —
superHeroes。 - 在其中,我們想訪問
members屬性,所以我們使用.members。 members包含一個由物件組成的陣列。我們想訪問陣列中的第二個物件,所以我們使用[1]。- 在這個物件中,我們想訪問
powers屬性,所以我們使用.powers。 powers屬性中是一個數組,其中包含選定英雄的超能力。我們想要第三個,所以我們使用[2]。
關鍵是,使用 JSON 並沒有什麼特別之處;在你將其解析為 JavaScript 物件之後,你就可以像處理使用相同物件字面量語法宣告的物件一樣處理它。
注意:我們已將上述 JSON 在我們的 JSONTest.html 示例中作為變數提供(請參閱原始碼)。嘗試載入它,然後透過瀏覽器的 JavaScript 控制檯訪問變數中的資料。
作為 JSON 的陣列
上面我們提到 JSON 文字基本上看起來像字串中的 JavaScript 物件。我們也可以將陣列轉換為/從 JSON。以下示例是完全有效的 JSON:
[
{
"name": "Molecule Man",
"age": 29,
"secretIdentity": "Dan Jukes",
"powers": ["Radiation resistance", "Turning tiny", "Radiation blast"]
},
{
"name": "Madame Uppercut",
"age": 39,
"secretIdentity": "Jane Wilson",
"powers": [
"Million tonne punch",
"Damage resistance",
"Superhuman reflexes"
]
}
]
你必須從陣列索引開始訪問陣列項(在其解析版本中),例如 superHeroes[0].powers[0]。
JSON 也可以包含單個原始值。例如,29、"Dan Jukes" 或 true 都是有效的 JSON。
JSON 語法限制
如前所述,任何 JSON 都是有效的 JavaScript 字面量(物件、陣列、數字等)。然而,反之則不然 — 並非所有 JavaScript 物件字面量都是有效的 JSON。
- JSON 只能包含可序列化的資料型別。這意味著:
- 對於原始型別,JSON 可以包含字串字面量、數字字面量、
true、false和null。值得注意的是,它不能包含undefined、NaN或Infinity。 - 對於非原始型別,JSON 可以包含物件字面量和陣列,但不能包含函式或任何其他物件型別,例如
Date、Set和Map。JSON 中的物件和陣列需要進一步包含有效的 JSON 資料型別。
- 對於原始型別,JSON 可以包含字串字面量、數字字面量、
- 字串必須用雙引號括起來,而不是單引號。
- 數字必須以十進位制表示法書寫。
- 物件的每個屬性必須是
"key": value的形式。屬性名稱必須是雙引號括起來的字串字面量。不允許使用特殊的 JavaScript 語法,例如方法,因為方法是函式,而函式不是有效的 JSON 資料型別。 - 物件和陣列不能包含尾隨逗號。
- JSON 中不允許有註釋。
即使是單個放錯位置的逗號或冒號也可能導致 JSON 檔案無效並使其失敗。你應該小心驗證你嘗試使用的任何資料(儘管計算機生成的 JSON 不太可能包含錯誤,只要生成程式正常工作)。你可以使用 JSONLint 或 JSON-validate 等應用程式驗證 JSON。
透過一個 JSON 示例進行操作
那麼,讓我們透過一個例子來展示我們如何在網站上利用一些 JSON 格式的資料。
入門
首先,複製我們的 heroes.html 和 style.css 檔案到本地。後者包含一些簡單的 CSS 來樣式化我們的頁面,而前者包含一些非常簡單的 HTML 主體,以及一個 <script> 元素來包含我們將在本練習中編寫的 JavaScript 程式碼。
<header>
...
</header>
<section>
...
</section>
<script>
// JavaScript goes here
</script>
我們的 JSON 資料已在 GitHub 上提供,網址為 https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json。
我們將把 JSON 載入到我們的指令碼中,並使用一些巧妙的 DOM 操作來顯示它,就像這樣:

頂級函式
頂級函式如下所示:
async function populate() {
const requestURL =
"https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json";
const request = new Request(requestURL);
const response = await fetch(request);
const superHeroes = await response.json();
populateHeader(superHeroes);
populateHeroes(superHeroes);
}
為了獲取 JSON,我們使用了一個名為 Fetch 的 API。這個 API 允許我們透過 JavaScript 向伺服器發出網路請求來檢索資源(例如影像、文字、JSON 甚至 HTML 片段),這意味著我們可以更新內容的小部分而無需重新載入整個頁面。
在我們的函式中,前四行使用 Fetch API 從伺服器獲取 JSON:
- 我們宣告
requestURL變數來儲存 GitHub URL。 - 我們使用 URL 初始化一個新的
Request物件。 - 我們使用
fetch()函式發出網路請求,它返回一個Response物件。 - 我們使用
Response物件的json()函式將響應檢索為 JSON。
注意: fetch() API 是非同步的。你可以在我們的非同步 JavaScript 模組中詳細瞭解非同步函式,但現在,我們只需說明我們需要在使用 fetch API 的函式名稱前新增關鍵字 async,並在呼叫任何非同步函式前新增關鍵字 await。
完成所有這些後,superHeroes 變數將包含基於 JSON 的 JavaScript 物件。然後,我們將該物件傳遞給兩個函式呼叫——第一個函式用正確的資料填充 <header>,而第二個函式為團隊中的每個英雄建立一個資訊卡,並將其插入到 <section> 中。
填充 header
既然我們已經檢索到 JSON 資料並將其轉換為 JavaScript 物件,那麼讓我們透過編寫上面提到的兩個函式來利用它。首先,在前面的程式碼下方新增以下函式定義:
function populateHeader(obj) {
const header = document.querySelector("header");
const myH1 = document.createElement("h1");
myH1.textContent = obj.squadName;
header.appendChild(myH1);
const myPara = document.createElement("p");
myPara.textContent = `Hometown: ${obj.homeTown} // Formed: ${obj.formed}`;
header.appendChild(myPara);
}
這裡我們首先使用 createElement() 建立一個 h1 元素,將其 textContent 設定為等於物件的 squadName 屬性,然後使用 appendChild() 將其附加到 header。然後,我們對一個段落執行非常類似的操作:建立它,設定其文字內容並將其附加到 header。唯一的區別是其文字被設定為一個模板字面量,其中包含物件的 homeTown 和 formed 屬性。
建立英雄資訊卡
接下來,在程式碼底部新增以下函式,它建立並顯示超級英雄卡片:
function populateHeroes(obj) {
const section = document.querySelector("section");
const heroes = obj.members;
for (const hero of heroes) {
const myArticle = document.createElement("article");
const myH2 = document.createElement("h2");
const myPara1 = document.createElement("p");
const myPara2 = document.createElement("p");
const myPara3 = document.createElement("p");
const myList = document.createElement("ul");
myH2.textContent = hero.name;
myPara1.textContent = `Secret identity: ${hero.secretIdentity}`;
myPara2.textContent = `Age: ${hero.age}`;
myPara3.textContent = "Superpowers:";
const superPowers = hero.powers;
for (const power of superPowers) {
const listItem = document.createElement("li");
listItem.textContent = power;
myList.appendChild(listItem);
}
myArticle.appendChild(myH2);
myArticle.appendChild(myPara1);
myArticle.appendChild(myPara2);
myArticle.appendChild(myPara3);
myArticle.appendChild(myList);
section.appendChild(myArticle);
}
}
首先,我們將 JavaScript 物件的 members 屬性儲存在一個新變數中。此陣列包含多個物件,這些物件包含每個英雄的資訊。
接下來,我們使用 for...of 迴圈遍歷陣列中的每個物件。對於每個物件,我們:
- 建立幾個新元素:一個
<article>、一個<h2>、三個<p>和一個<ul>。 - 設定
<h2>以包含當前英雄的name。 - 用他們的
secretIdentity、age以及一句“超能力:”來介紹列表中的資訊。 - 將
powers屬性儲存在另一個名為superPowers的新常量中 — 它包含一個列出當前英雄超能力的陣列。 - 使用另一個
for...of迴圈遍歷當前英雄的超能力 — 對於每個超能力,我們建立一個<li>元素,將超能力放入其中,然後使用appendChild()將listItem放入<ul>元素 (myList) 中。 - 我們做的最後一件事是將
<h2>、<p>和<ul>附加到<article>(myArticle) 中,然後將<article>附加到<section>中。附加的順序很重要,因為這是它們在 HTML 中顯示的順序。
注意:如果你在嘗試使示例正常工作時遇到問題,請嘗試參考我們的 heroes-finished.html 原始碼(也可以檢視其執行情況。)
注意:如果你在遵循我們用於訪問 JavaScript 物件的點/方括號表示法時遇到困難,可以嘗試在另一個選項卡或文字編輯器中開啟 superheroes.json 檔案,並在檢視我們的 JavaScript 時參考它。你也應該參考我們的 JavaScript 物件基礎知識文章,以獲取有關點和方括號表示法的更多資訊。
呼叫頂級函式
最後,我們需要呼叫我們的頂級 populate() 函式:
populate();
在物件和文字之間轉換
上面的示例在訪問 JavaScript 物件方面很簡單,因為我們使用 response.json() 將網路響應直接轉換為 JavaScript 物件。
但有時我們沒有那麼幸運——有時我們收到一個原始 JSON 字串,我們需要自己將其轉換為物件。當我們想透過網路傳送一個 JavaScript 物件時,我們需要在傳送之前將其轉換為 JSON(一個字串)。幸運的是,這兩個問題在 Web 開發中非常常見,因此瀏覽器中提供了一個內建的 JSON 物件,其中包含以下兩種方法:
parse():接受一個 JSON 字串作為引數,並返回相應的 JavaScript 物件。stringify():接受一個物件作為引數,並返回等效的 JSON 字串。
你可以在我們的 heroes-finished-json-parse.html 示例中看到第一個的實際應用(請參閱原始碼)——這與我們之前構建的示例完全相同,只是:
- 我們透過呼叫響應的
text()方法來以文字而不是 JSON 的形式檢索響應。 - 然後我們使用
parse()將文字轉換為 JavaScript 物件。
關鍵程式碼片段在這裡:
async function populate() {
const requestURL =
"https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json";
const request = new Request(requestURL);
const response = await fetch(request);
const superHeroesText = await response.text();
const superHeroes = JSON.parse(superHeroesText);
populateHeader(superHeroes);
populateHeroes(superHeroes);
}
正如你可能猜到的,stringify() 的工作方式恰恰相反。嘗試將以下行逐行輸入到瀏覽器的 JavaScript 控制檯中,以檢視其執行情況:
let myObj = { name: "Chris", age: 38 };
myObj;
let myString = JSON.stringify(myObj);
myString;
這裡我們正在建立一個 JavaScript 物件,檢查它包含什麼,使用 stringify() 將其轉換為 JSON 字串——將返回值儲存在一個新變數中——然後再次檢查它。
總結
在本課程中,我們向你介紹瞭如何在程式中使用 JSON,包括如何建立和解析 JSON,以及如何訪問其中鎖定的資料。在下一篇文章中,我們將為你提供一些測試,你可以用來檢查你對所有這些資訊的理解和記憶程度。