数组、对象与 JSON · 前端:HTML、CSS 与 JavaScript
怎么处理一堆东西,比如商店里的商品?
课程目录为什么需要它
网页展示的数据,几乎都是以对象数组的形式送来的:商品、消息、搜索结果。把数据变成屏幕上的内容,就是筛选、变换、汇总,而 filter、map、reduce 每个都能用一行好读的代码做到。
直觉理解
对象把有名字的值放在一起;数组是有顺序的列表:
const lamp = { name: "Lamp", price: 30 };
const prices = [30, 120, 45];用点来读值:lamp.price;用位置取元素:prices[0]。
数组方法接收一个小函数,作用在每个元素上,返回一个新的结果(原数组不变):
| 方法 | 返回 |
|---|---|
filter(x => 条件) | 条件为 true 的那些元素 |
map(x => 新值) | 每个元素对应一个新值 |
reduce((acc, x) => …, 初始值) | 用所有元素算出的一个值 |
find(x => 条件) | 第一个符合的元素,或者 undefined |
...(展开)用来复制:[...list, item] 是多了一个元素的新数组,{ ...obj, price: 9 } 是改了一个字段的新对象。
JSON 是对象在网络上传输的方式:JSON.stringify(obj) 把对象变成文字,JSON.parse(text) 再变回来。
定义
数组方法
A function such as filter, map or reduce that walks the array for you and returns a new result.filter、map、reduce 这类函数,替你遍历数组并返回新结果。
展开(...)
Copies the items of an array or the fields of an object into a new one.把数组的元素或对象的字段复制到一个新的数组或对象里。
JSON
A text format for data: objects in { }, arrays in [ ], strings in double quotes. APIs send and receive it.一种表示数据的文字格式:对象用 { },数组用 [ ],字符串用双引号。API 收发的就是它。
示例
先筛选,再变换
动手试试留下有货的商品,再只留下名字。
const products = [
{ name: "Lamp", price: 30, inStock: true },
{ name: "Desk", price: 120, inStock: false },
{ name: "Chair", price: 45, inStock: true },
];
const available = products.filter(p => p.inStock);
const names = available.map(p => p.name);
console.log(names);
console.log(products[1].price);[ 'Lamp', 'Chair' ] 120
- L6留下 Lamp 和 Chair:p.inStock 为 true 的那些。
- L7把每个商品变成它的名字。
- L8数组里的字符串打印时带引号。
[ 'Lamp', 'Chair' ] - L9原数组还是三个元素;下标 1 是桌子。
120
加起来
动手试试reduce 带着一个累计值,一个元素一个元素地往下走。
const prices = [30, 120, 45];
const total = prices.reduce((sum, price) => sum + price, 0);
console.log(total);
console.log(Math.max(...prices));195 120
- L2sum 从 0 开始,然后 0+30、30+120、150+45。
195 - L4...prices 把数组展开成多个参数:Math.max(30, 120, 45)。
120
复制与 JSON
动手试试改副本,不改原件;再把对象变成文字。
const user = { name: "Ada", city: "London" };
const moved = { ...user, city: "Paris" };
console.log(user.city, moved.city);
const { name, city } = moved;
console.log(name, city);
const text = JSON.stringify(moved);
console.log(text);
console.log(JSON.parse(text).name);London Paris
Ada Paris
{"name":"Ada","city":"Paris"}
Ada
- L2一个新对象:先复制 user 的所有字段,再替换 city。
- L3原对象没有变。
London Paris - L5解构赋值把字段取出来放进变量。
- L8JSON 文字:键和字符串都用双引号。
{"name":"Ada","city":"Paris"}
常见错误
有花括号却没写 return
x => x * 2 会返回值,但 x => { x * 2 } 什么都不返回(undefined)。用了花括号就要写 return。
sort() 按文字排序
[10, 9, 1].sort() 得到 [1, 10, 9]。数字要传比较函数:.sort((a, b) => a - b)。
用 === 比较对象
{a: 1} === {a: 1} 是 false:对象用 === 比较,问的是「是不是同一个对象」,不是「内容一不一样」。
小测验
1. 这段代码会输出什么?
const doubled = [1, 2, 3].map(x => { x * 2 });
console.log(doubled);查看答案
答案:B
花括号让它变成了没有 return 的函数体,所以每个元素都变成了 undefined。
2. 这段代码会输出什么?
console.log([10, 9, 1].sort());查看答案
答案:B
没有比较函数时,sort 按文字比较:"10" 排在 "9" 前面,因为 "1" < "9"。
动手练习
3 道练习,分三层:先预测程序的输出,再按新需求改程序,最后从零写一个。代码在浏览器里运行,提交马上判题。
- 预测输出购物车里有什么?
准确写出它打印的内容。
- 改需求活跃用户的名字
activeNames应该返回活跃用户的名字,现在却返回一串undefined。修好它。 - 从零写每个顾客的总额
写一个
totalsByCustomer(orders)。每个订单形如{ customer: "ann", amount: 20 }。返回一个对象,记录每个顾客的总额:totalsByCustomer([ { customer: "ann", amount: 20 }, { customer: "bo", amount: 5 }, { customer: "ann", amount: 10 }, ]) // { ann: 30, bo: 5 }
本课术语
| 术语 | 中文 | 英文定义 |
|---|---|---|
| Array | 数组 | An ordered list of values: [1, 2, 3]. |
| Object | 对象 | Named values grouped together: { name: "Ada" }. |
| JSON | JSON | A text format for data, used by almost every web API. |
| Spread | 展开运算符 | ... copies the items of an array or the fields of an object. |