返回笔记列表
01 · NOTES / WebGIS 开发

第 12 节 · 对象与数组

2026 年 9 月 1 日

第 12 节 · 对象与数组

📌 版本信息:基于 ECMAScript 2025(基础语义多年稳定;2026-08-29 核对) 📚 来源:现代 JS 教程 · 对象现代 JS 教程 · 数组MDN 解构赋值

一、这一节的目标

  1. 精通对象的读写:点、方括号、嵌套、可选链 ?.
  2. 精通数组常用操作与可变/不可变两种思路
  3. 掌握解构与展开运算符(现代 JS 代码的"方言",读不懂它们就读不懂 AI 写的代码)
  4. 理解引用类型的赋值/比较行为(一个必踩的坑提前拆掉)
  5. 能读懂 GeoJSON 的结构——你未来天天打交道的地图数据格式

二、对象:键值对的集合

// 字面量写法:GIS 数据建模的第一形态
const wuhan = {
  name: '武汉',
  location: { lat: 30.5929, lng: 114.3052 },   // 嵌套对象
  population: 1233,       // 万人
  tags: ['省会', '江城'], // 值也可以是数组
};

// 读:点运算符(常用)/ 方括号(键是变量时必须用)
wuhan.name               // '武汉'
wuhan.location.lat       // 30.5929
const key = 'name';
wuhan[key]               // '武汉'(点写法在这里会找名为 'key' 的属性,错)
wuhan['na' + 'me']       // 拼出来的键只能用方括号

// 写与增:直接赋值
wuhan.area = 8569;       // 新增属性
wuhan.population = 1373; // 修改

// 删
delete wuhan.area;

// 可选链 ?:安全地读"可能不存在"的深层属性
const beijing = { name: '北京' };
beijing.location?.lat    // undefined(不报错!没有 ?。 则直接 TypeError)
// 读外部 GeoJSON 数据时的保命符:别人的数据字段未必齐全

// 遍历
Object.keys(wuhan)       // ['name', 'location', 'population', 'tags']
Object.values(wuhan)     // 值的数组
Object.entries(wuhan)    // [['name','武汉'], [...]] 键值对数组

三、数组:有序列表

const cities = ['武汉', '西安', '成都'];

cities.length            // 3
cities[0]                // '武汉'(下标从 0 开始!最后一个:length - 1)
cities[cities.length - 1]  // '成都'

// 尾部操作(最高频)
cities.push('北京');      // 尾部加,返回新长度
cities.pop();            // 尾部删,返回删掉的元素
cities.unshift('上海');   // 头部加
cities.shift();          // 头部删

// 查找与判断
cities.includes('武汉')    // true
cities.indexOf('西安')     // 1(找不到返回 -1)

// 切片与拼接(都不改原数组!)
const a = [1, 2, 3, 4, 5];
a.slice(1, 3)            // [2, 3]:切出 [1,3) 段
const b = a.concat([6]); // [1,2,3,4,5,6]

// 排序:注意!sort 默认按字符串排,数字要传比较函数
const nums = [30, 100, 4];
nums.sort()                       // [100, 30, 4] ← 字典序,经典坑
nums.sort((x, y) => x - y)        // [4, 30, 100] ✓ 升序
nums.sort((x, y) => y - x)        // 降序
// ⚠️ sort() 默认会改原数组!不想改就先 [...nums].sort(...)

四、解构与展开:现代 JS 的方言

读 AI 写的代码,这两个符号出现率 100%:

// ── 解构:从对象/数组里"按形状"拆值 ──
const city = { name: '武汉', lat: 30.59, lng: 114.3 };

const { name, lat } = city;            // name='武汉', lat=30.59(按属性名拆)
const { name: cityName } = city;       // 重命名:cityName='武汉'
const { zoom = 10 } = { };             // 带默认值:zoom=10

const [first, second] = cities;        // 数组按位置拆:first='武汉'

// 函数参数解构(第 11 节的"对象参数"就是它)
function moveMarker({ lat, lng }) { /* ... */ }
moveMarker({ lat: 30, lng: 114 });

// ── 展开运算符 ...:把"打包的"拆开铺平 ──
const base = { name: '武汉' };
const updated = { ...base, population: 1373 };
// { name:'武汉', population:1373 } —— 常用:不改原对象,造一个改好的新对象(不可变更新)

const more = [...cities, '北京'];      // 数组拼接新写法
const [head, ...rest] = cities;        // rest = ['西安','成都']

💡 不可变更新的意义:React(第 03 模块)依赖"数据变没变"来决定界面更新不更新,{...obj, key: value} 这种"造新的而不是改旧的"写法是 React 状态更新的标准动作,现在先养成手感。


五、引用类型的坑:数组/对象是"地址"

原始类型(number/string/bool)存"值本身";对象/数组存的是地址

let a = 5;
let b = a;       // b 复制了值 5
b = 10;          // a 还是 5,互不相干 ✓

const arr1 = [1, 2, 3];
const arr2 = arr1;    // arr2 复制的是"地址"——指向同一个数组!
arr2.push(4);
console.log(arr1);    // [1, 2, 3, 4] ← arr1 也变了!!

const obj1 = { name: '武汉' };
const obj2 = { ...obj1 };   // 展开 = 浅拷贝,造了个新对象
obj2.name = '西安';
console.log(obj1.name);     // '武汉' ✓ 这次没受影响
  • 浅拷贝{...obj} / [...arr]):复制第一层;如果属性里还有嵌套对象,里面仍是共享地址
  • 深拷贝structuredClone(obj)(现代内置,推荐)彻底复制所有层

六、读懂 GeoJSON(本节的高潮)

GeoJSON 就是"对象 + 数组"的套娃,现在你已经具备读懂它的全部知识

const quake = {
  type: 'FeatureCollection',          // 顶层固定写法:要素集合
  features: [                         // 数组:装着一堆"要素"
    {
      type: 'Feature',
      geometry: {                     // 几何:在哪
        type: 'Point',
        coordinates: [114.3052, 30.5929],  // ⚠️ 经度在前,纬度在后!(数学里 xy 的习惯)
      },
      properties: {                   // 属性:是什么
        mag: 5.1,                     // 震级
        place: '湖北武汉',
        time: 1756444800000,          // 时间戳(毫秒)
      },
    },
    // ... 还可以有第二个、第 n 个 Feature
  ],
};

// 现在用你刚学的知识取值:
quake.features[0].properties.mag          // 5.1
quake.features[0].geometry.coordinates[0] // 114.3052(经度)
quake.features[0]?.properties?.place      // '湖北武汉'(可选链保平安)

第 04 模块会把 GeoJSON 规范完整过一遍;USGS 地震数据(m00 练习用过的)就是这个形状——你已经能徒手解剖它了


七、动手跟练:12 · 学生成绩管理

配套文件:02-web-basics/examples/12-学生成绩管理.html(Console 驱动的数据练习,DOM 只负责展示)

步骤:

  1. Live Server 打开,F12 看 Console,对照代码读每一步输出
  2. 数据是一组"学生对象数组"(和 GeoJSON 的形状神似)
  3. 完成 6 个 TODO:查不及格人数、找最高分学生、给某人加一门课成绩、展开运算造新数组不改原数据、structuredClone 深拷贝、把学生列表渲染成表格

通关标准:

  • 不查资料说出引用类型的赋值坑现象
  • 能解释"浅拷贝后嵌套对象仍共享"的原因
  • 能徒手画出 GeoJSON 的结构树(type → features → geometry/properties)

八、自测题

  1. city['na'+'me']city.name 都能取到 name,什么时候必须用方括号?
  2. [100, 30, 4].sort() 的结果是什么?为什么?
  3. {...obj, age: 18} 做了什么?为什么 React 时代偏好这种写法?
  4. 浅拷贝和深拷贝的区别?现代深拷贝用哪个内置函数?
  5. GeoJSON 的 coordinates 里经度和纬度谁在前?

参考答案

  1. 键是变量/表达式拼出来的、或键含特殊字符时,必须方括号。
  2. [100, 30, 4]——sort 默认按字符串字典序,'100' < '30' < '4'。
  3. 浅拷贝出一个新对象并把 age 覆盖/新增为 18,原对象不动;React 靠"引用是否变化"判断是否重渲染,改旧对象会骗过它。
  4. 浅拷贝只复制第一层,嵌套引用类型仍共享;深拷贝用 structuredClone()
  5. 经度在前(GeoJSON 规定 [lng, lat],与日常"纬度,经度"的习惯相反,地图库对接时的高频事故点)。

九、下一步

数据会组织了,但处理数据还在用 for 循环裸写 → 第 13 节:数组高阶方法,map/filter/reduce 三板斧让你像写句子一样处理数据。

TAGSweb