社区文档构建进行中,欢迎编辑。社区答疑群(非官方):717421103,点点小课堂(腾讯会议):5696651544
全站通知:
帮助:使用JSON动态更新页面
刷
历
编
跳到导航
跳到搜索
须知
- 作者会尽量采用通俗的语言来介绍,但涉及到核心的部分依然需要你懂得一定的编程及前端知识。
- 本文中的代码块,绿色底表示正确,红色底表示错误,黄色底表示警告。
前言
什么是JSON
- JSON是JS通用的外部数据储存格式,其形式是对象或数组,形如:
{ "键": "值" }
- 或
[ "值" ]
- 为了便于展示,本文大多数情况会采用对象作为示例,但你应该知道数组也是JSON中重要的形式,按照需求设计结构会让你事半功倍。
- 其实,数组本就是一种特殊的对象,数组的键就是元素的位置(index),这里不展开介绍了。
规范
根对象/根数组
- 一个JSON必须有且只有一个最外层对象/数组,就如同开篇的两个样例。
"无根的键": "值",
{ "键": "值" } { "键": "值" }
分隔
- 键与值之间必须使用英文冒号
:
进行分隔。 - 一条内容之间必须使用英文逗号
,
进行分隔,最后一条内容后不能加逗号。
{ "键1": "值1", "键2": "值2" }
{ "键1": "值1" "键2": "值2" }
{ "键1": "值1", "键2": "值2", }
格式化
- JSON对换行、缩进(空格/制表符)均没有要求:
{ "键1": "值1", "键2": "值2" }
{ "键1":"值1", "键2":"值2" }
- 请不要学习以上两种写法。
{"键1":"值1","键2":"值2"}
- 以上便是压缩后的JSON。
(对象的)键
- 键必须是使用英文双引号
""
包裹的字符串:
{ '单引号': "值" }
- 在一个对象内,不能有同名的键:
{ "同名的键": "值1", "同名的键": "值2" }
值
- 值是可以重复的。
{ "键1": "值", "键2": "值", "键3": "值" }
- 值可以储存以下数据:
字符串
{ "字符串": "你好,世界。" }
- 字符串值和键一样,必须使用英文双引号
""
包裹:
{ "字符串": '单引号,不行。' }
数字
{ "整数": 5, "浮点数": 3.1416, "科学计数": 6.02e23 }
{ "其他进制表示法": 0xf2dede, "极限值": Infinity }
真假(布尔)
{ "布尔真": true, "布尔假": false }
空(null)
{ "空": null }
对象/数组
{ "子对象": { "子对象的键": "子对象的值" }, "子数组": [ "子数组的值1", "子数组的值2" ] }
- 基于此特点,可以设计出复杂的结构来储存大量的数据:
{ "table": { "id": null, "class": ["wikitable","test"] }, "headLine": [ "序号", "事件", "发生时间", "是否完成" ], "lines": [ [ 1, "起床", 1660608000000, true ], [ 2, "吃饭", 1660608900000, true ], [ 3, "上车", 1660609800000, true ], [ 4, "上班", 1660613400000, true ], [ 5, "下班", 1660645800000, false ] ] }
- 事实上,JSON可以不需要根,仅单独存一个值,但这几乎只在极少数情况下有用,本文之后也不会介绍。
- 其实,字符串、数字、布尔、空,也是特殊的对象,这里也不展开介绍了。
"值"
1
false
null
与JS对象的关系
- 在阅读本节内容之前,请你先复述这句话无数遍:
- JSON对于JS来说是字符串。
- JSON 对于JS来说 是字符串。
- JSON 对于 JS 来说是 字符串。
- 如果你无法理解这句话的含义,可能会对后续的阅读产生障碍。
- 当然,你也可以尝试带着疑问继续阅读。
互相转化
- JSON可以通过JS的函数
JSON.parse
转为JS对象。
JSON.parse('{"键":"值"}') //输出:一个JS对象
- 这个动作称为解析。
- JS对象可以通过JS的函数
JSON.stringify
转为JSON。
let obj={ "键": "值" } JSON.stringify(obj) //输出:'{"键":"值"}'
- 这个动作称为序列化。
不同点
- JSON无法储存JS对象中的函数,序列化一个含有函数的JS对象时,会被舍弃:
let obj={ 键:"值", 函数:function(){ return "你好,世界。" } } JSON.stringify(obj) //输出:'{"键":"值"}'
- 总体来说,序列化一个JS对象时,只有 $值 中提到的类型可以保留,其他的可能变成空(null),可能被舍弃,也可能变成空对象:
let obj={ 极限:Infinity, 标签:Symbol(), DOM元素:document.createElement("div") } JSON.stringify(obj) //输出:'{"极限":null,"DOM元素":{}}'
正文
- 在谷歌浏览器等现代浏览器中均可以在控制台中直接运行JS,如果方便的话,请你按下F12打开控制台,跟着作者一步步操作。
- 使用函数
console.log
输出结果:
- 使用函数
console.log("你好,世界。") //输出:你好,世界。
- 本文后续的所有的内容不再像 $与JS对象的关系 中直接输出结果了,而是通过
console.log
输出结果。
读取JSON
- JSON最常见的形式有两种:静态文件(*.json)和请求结果(HttpRequest)。
- 前端实际操作都是通过让JS访问一个地址(URL),获取到了JSON的字符串,再将其解析为JS对象后使用。
访问地址(异步)
- 作者已经准备好了一个JSON,就放在 https://wiki.biligame.com/tools/index.php?title=MediaWiki:JSONcourse.json&action=raw 这里,你可以先通过浏览器直接看到它的内容。
原生JS
let url = "https://wiki.biligame.com/tools/index.php?title=MediaWiki:JSONcourse.json&action=raw" let ajax = new XMLHttpRequest() ajax.open("GET", url) ajax.send() let obj = JSON.parse(ajax.responseText) console.log(obj) //报错
- 为什么在控制台中没有结果?
- 原来这是一个异步操作。顾名思义,异步是不同步的,JS不会立刻执行异步操作中的代码,而是等待谁来告诉它“你可以继续了”才会继续执行。
- 在上述代码中,谁就是服务器。服务器把JSON发过来需要时间,虽然bilibili的服务器很快很快,但在你敲下回车的一瞬间,JS就已经执行完了同步的部分,服务器还没快到这种程度呀。
- 同步中的
ajax.responseText
还是空,JSON.parse(ajax.responseText)
等同于JSON.parse(null)
,这便是报错的来源。 - 综上所述,不能直接同步执行
JSON.parse(ajax.responseText)
,而是告诉JS,“在服务器把JSON发过来之后,再执行JSON.parse(ajax.responseText)
”:
let url = "https://wiki.biligame.com/tools/index.php?title=MediaWiki:JSONcourse.json&action=raw" let ajax = new XMLHttpRequest() ajax.open("GET", url) ajax.send() ajax.onreadystatechange = function () { if (ajax.readyState === 4) { if (ajax.status === 200) { let obj = JSON.parse(ajax.responseText) console.log(obj) } } } //输出:那个JSON解析后的JS对象
jQuery
- jQuery是一个JS的库,功能很强大,BWIKI安装了,就正好利用一下吧。
console.log($) //输出:ƒ (selector,context){return new jQuery.fn.init(selector,context);}
- 如果你看到了上述结果,说明jQuery是生效的。
console.log($) //输出:ƒ $() { [native code] }
- 如果你看到了上述结果,说明jQuery没有生效。
let url = "https://wiki.biligame.com/tools/index.php?title=MediaWiki:JSONcourse.json&action=raw" $.getJSON(url, function (obj) { console.log(obj) }) //输出:那个JSON解析后的JS对象
- 注意:
$.getJSON
会自动将JSON解析为JS对象,无需手动使用JSON.parse
。 - 因为jQuery无论是获取JSON还是操作DOM元素都实在是太方便了,而且BWIKI也能用,所以本文后续所有的读取JSON都会用jQuery来完成。
查值
- JS将JSON解析后,可以像查JS对象一样查JSON的值。
基本
- 对象:
- 使用英文点
.
加键
组成对象.键
,或者英文方括号[]
包裹键的字符串对象["键"]
,来读取键对应的值:
- 使用英文点
let json = '{"key1":"值1","key2":"值2"}' let obj = JSON.parse(json) console.log(obj.key1) //或:console.log(obj["key1"]) //输出:值1
- 数组:
- 使用英文方括号
[]
包裹值所在的位置数组[位置]
,来读取对应位置的值:
- 使用英文方括号
let json = '["零","一","二"]' let arr = JSON.parse(json) console.log(arr[0]) //输出:零
- 注意,数组的第一个元素的位置是0,最后一个元素的位置是长度-1。
- 或许你听过这个段子:程序员数数都是从0开始数的。
- 来点复杂的:
let json = `{ "arr": [ "零", "一", { "inner": "二" } ] }` let obj = JSON.parse(json) console.log(obj.arr[2].inner) //输出:二
遍历
- 对象:
- 形如
for (let 键 in 对象) {}
,让JS访问对象中的所有内容,并执行{}
中的动作:
- 形如
let json = `{ "键1": "值1", "键2": "值2", "键3": "值3" }` let obj = JSON.parse(json) for (let key in obj) { console.log(key) console.log(obj[key]) //请思考一下这里为什么不能用obj.key。 } //输出:键1 //输出:值1 //输出:键2 //输出:值2 //输出:键3 //输出:值3
- 数组:
- 形如
for (let 位置 = 0; 位置 < 数组.length; 位置++) {}
,让JS访问数组中的所有内容,并执行{}
中的动作:
- 形如
let json = '["零","一","二"]' let arr = JSON.parse(json) for (let i = 0; i < arr.length; i++) { console.log(arr[i]) } //输出:零 //输出:一 //输出:二
让JSON按照你的想法展示给用户
- 现在,是时候正式阅读一下这个JSON了:https://wiki.biligame.com/tools/index.php?title=MediaWiki:JSONcourse.json&action=raw 。
- 相信你不难看出,这个JSON描述了一个表格:
- 表格本身拥有
class="wikitable test"
; - 表格拥有1个标题行,标题行有4个单元格,内容分别是“序号”、“事件”、“发生时间”、“是否完成”;
- 表格拥有5个内容行,描述了标题行中对应的东西。
- 表格本身拥有
- 就像这样:
序号 | 事件 | 发生时间 | 是否完成 |
---|---|---|---|
1 | 起床 | 1660608000000 | true |
2 | 吃饭 | 1660608900000 | true |
3 | 上车 | 1660609800000 | true |
4 | 上班 | 1660613400000 | true |
5 | 下班 | 1660645800000 | false |
不要使用WIKI文本
- 通过WIKI文本写出上面这个表格是很简单的
{|class="wikitable test" |- !序号||事件||发生时间||是否完成 |- |1||起床||1660608000000||true |- |2||吃饭||1660608900000||true |- |3||上车||1660609800000||true |- |4||上班||1660613400000||true |- |5||下班||1660645800000||false |}
- 但,JS看不懂WIKI文本,它只能看懂HTML:
<table class="wikitable test"> <tr> <th>序号</th> <th>事件</th> <th>发生时间</th> <th>是否完成 </th> </tr> <tr> <td>1</td> <td>起床</td> <td>1660608000000</td> <td>true </td> </tr> <tr> <td>2</td> <td>吃饭</td> <td>1660608900000</td> <td>true </td> </tr> <tr> <td>3</td> <td>上车</td> <td>1660609800000</td> <td>true </td> </tr> <tr> <td>4</td> <td>上班</td> <td>1660613400000</td> <td>true </td> </tr> <tr> <td>5</td> <td>下班</td> <td>1660645800000</td> <td>false </td> </tr> </table>
DOM操作
获取
- ↓这里放了一个
<div>
元素,它拥有id="testDiv1"
。
- ↑你现在肯定看不见它,因为它没有任何内容,也没有任何样式:
<div id="testDiv1"></div>
- 看不见不代表不存在,你现在可以通过JS来发现它:
- 再次强调,接下来本文使用的均为jQuery库支持的写法。
let div=$("#testDiv1") console.log(div) //输出:jQuery.fn.init [div#testDiv1]
- 尝试往这个
<div>
元素中写一句话:
let div=$("#testDiv1") div.append("你好,世界。")
- 这下就看见它了。
创建
- DOM元素中可以包含另一个DOM元素。
- 先用JS来创建一个DOM元素。
let newDom=$('<span class="newDom">') console.log(newDom) //输出:jQuery.fn.init [span.newdom]
- JS创建的DOM也可以变化,试试往里面输入一些文字,然后改变一下它的文字颜色和字号
let newDom=$('<span class="newDom">') newDom.append("你好,世界") newDom.css({ "color": "red", "font-size": "2em" })
- ↓
- ↑这里放了一个
<div>
元素:
<div id="testDiv2"></div>
- 尝试把新的DOM放进上面这个
<div>
元素:
let newDom=$('<span class="newDom">') newDom.append("你好,世界。") newDom.css({ "color": "red", "font-size": "2em" }) let div=$("#testDiv2") div.append(newDom)
- jQuery还提供一种更便捷的写法:
let newDom=$('<span class="newDom" style="color:red;font-size:2em">你好,世界。</span>')
- 可以直接将表示DOM元素信息的字符串直接变成DOM元素,对创建一些简单的DOM元素会更加方便。
清空
- ↓
你好,世界。
- ↑这里放了一个
<div>
元素:
<div id="testDiv3">你好,世界。</div>
- 尝试把上面这个
<div>
元素清空:
let div=$("#testDiv3") div.empty()
- 你看不见它了,但它依然还存在。
实例演示
let url="https://wiki.biligame.com/tools/index.php?title=MediaWiki:JSONcourse.json&action=raw" $.getJSON(url, function (obj) { console.log(obj) }) //输出:那个JSON解析后的JS对象
- 创建HTML:
let url = "https://wiki.biligame.com/tools/index.php?title=MediaWiki:JSONcourse.json&action=raw" $.getJSON(url, function (obj) { let className = obj.table.class.join(" ") let html = `<table class="${className}">` html += `<tr>` for (let i = 0; i < obj.headLine.length; i++) { let headLineCell = obj.headLine[i] html += `<th>${headLineCell}</th>` } html += `</tr>` for (let i = 0; i < obj.lines.length; i++) { let line = obj.lines[i] html += `<tr>` html += `<td>${line[0]}</td>` html += `<td>${line[1]}</td>` html += `<td>${line[2]}</td>` html += `<td>${line[3]}</td>` html += `</tr>` } html += `</table>` console.log(html) }) //输出:<table class="wikitable test"><tr><th>序号</th><th>事件</th><th>发生时间</th><th>是否完成</th></tr><tr><td>1</td><td>起床</td><td>1660608000000</td><td>true</td></tr><tr><td>2</td><td>吃饭</td><td>1660608900000</td><td>true</td></tr><tr><td>3</td><td>上车</td><td>1660609800000</td><td>true</td></tr><tr><td>4</td><td>上班</td><td>1660613400000</td><td>true</td></tr><tr><td>5</td><td>下班</td><td>1660645800000</td><td>false</td></tr></table>
- HTML对缩进也不敏感。
- ↓
- ↑这里放了一个
<div>
元素:
<div id="tableDiv1"></div>
- 尝试把这段HTML放进上面这个
<div>
元素:
let url = "https://wiki.biligame.com/tools/index.php?title=MediaWiki:JSONcourse.json&action=raw" $.getJSON(url, function (obj) { console.log(obj) let className = obj.table.class.join(" ") let html = `<table class="${className}">` html += `<tr>` for (let i = 0; i < obj.headLine.length; i++) { let headLineCell = obj.headLine[i] html += `<th>${headLineCell}</th>` } html += `</tr>` for (let i = 0; i < obj.lines.length; i++) { let line = obj.lines[i] html += `<tr>` html += `<td>${line[0]}</td>` html += `<td>${line[1]}</td>` html += `<td>${line[2]}</td>` html += `<td>${line[3]}</td>` html += `</tr>` } html += `</table>` let div = $("#tableDiv1") div.append(html) })
- ↓
- ↑这里放了一个
<div>
元素:
<div id="tableDiv2"></div>
- 你可能会希望把“发生时间”这一列换成“YYYY.MM.DD hh:mm:ss”这样的格式,还希望把“是否完成”这一列换成“是”或“否”:
let url = "https://wiki.biligame.com/tools/index.php?title=MediaWiki:JSONcourse.json&action=raw" $.getJSON(url, function (obj) { console.log(obj) let className = obj.table.class.join(" ") let html = `<table class="${className}">` html += `<tr>` for (let i = 0; i < obj.headLine.length; i++) { let headLineCell = obj.headLine[i] html += `<th>${headLineCell}</th>` } html += `</tr>` for (let i = 0; i < obj.lines.length; i++) { let line = obj.lines[i] html += `<tr>` html += `<td>${line[0]}</td>` html += `<td>${line[1]}</td>` let time = new Date(line[2]) let YYYY = time.getFullYear() let MM = (time.getMonth() + 1 + "").padStart(2, "0") let DD = (time.getDate() + "").padStart(2, "0") let hh = (time.getHours() + "").padStart(2, "0") let mm = (time.getMinutes() + "").padStart(2, "0") let ss = (time.getSeconds() + "").padStart(2, "0") let timeStr = `${YYYY}.${MM}.${DD} ${hh}:${mm}:${ss}` html += `<td>${timeStr}</td>` let isDo if (line[3]) { isDo = "是" } else { isDo = "否" } html += `<td>${isDo}</td>` html += `</tr>` } html += `</table>` let div = $("#tableDiv2") div.empty() div.append(html) })