在日常开发中,我们经常需要快速创建指定长度的数组 、批量生成结构化数据 ,最常用的两个 API 就是 Array.from 和 Array.fill。
很多新手容易踩坑:用 Array.fill 创建对象数组出现数据联动篡改、无法生成带索引的唯一数据、不知道两种 API 的适用场景。
今天一文讲透两者的基础用法、实战案例、底层原理、核心区别、避坑方案,彻底搞定数组初始化问题。
一、前置知识点:new Array() 的缺陷
首先我们先了解为什么不直接用 new Array(n) 创建数组:
javascript
const arr = new Array(3)
console.log(arr) // [empty × 3] 稀疏数组
console.log(arr[0]) // undefined
arr.map(item => item + 1) // 无效,遍历不到 empty 空位
问题 :new Array() 创建的是稀疏数组(empty 空位) ,无法被 map、forEach 正常遍历,几乎无法直接使用。
所以我们需要 Array.from 和 Array.fill 将稀疏数组转为可正常遍历的密集数组。
二、Array.fill 用法详解
1. 基础语法
arr.fill(value, start?, end?)
-
作用:用一个固定值,填充数组的指定区间
-
特性 :原地修改原数组
-
默认区间:不传 start、end 则填充整个数组
2. 基础示例(基本数据类型)
javascript
// 创建长度为5、全部为0的数组
const arr = new Array(5).fill(0)
console.log(arr) // [0, 0, 0, 0, 0]
// 区间填充:下标1~3填充10
const arr2 = [1,2,3,4,5] arr2.fill(10, 1, 4)
console.log(arr2) // [1, 10, 10, 10, 5]
3. 致命坑:引用类型共享地址(高频踩坑)
fill 填充对象、数组 等引用类型时,所有元素指向同一个内存地址,改一个全联动!
javascript
// 错误示范!!!
const list = new Array(3).fill({ name: "测试" })
list[0].name = "修改第一个"
console.log(list) // [{name: '修改第一个'}, {name: '修改第一个'}, {name: '修改第一个'}]
核心问题:fill 只会「赋值」,不会「新建对象」,所有元素共用同一个引用地址。
4. fill 适用场景
-
快速生成固定长度的纯数字、字符串、布尔值数组
-
批量重置数组内容、区间覆盖数组值
-
不适合:生成对象数组、需要唯一数据、需要索引的场景
三、Array.from 用法详解
1. 基础语法
Array.from(类数组/可迭代对象, 映射回调?)
-
作用 :将类数组、稀疏数组转为密集数组,支持遍历映射
-
特性 :返回新数组,不修改原数据
-
回调优势 :自带
(item, index)索引参数,支持自定义每一项数据
2. 基础用法:填充基础值
javascript
// 生成长度5、全为1的数组
const arr = Array.from({ length: 5 }, () => 1) console.log(arr) // [1,1,1,1,1]
3. 核心优势:生成独立对象数组(无引用联动)
这是 Array.from 吊打 fill 的核心场景!回调函数每次执行都会生成全新对象,内存地址独立。
javascript
// 正确生成独立对象数组
const list = Array.from({ length: 3 }, (_, index) => ({ id: index, title: `列表${index + 1}` }))
list[0].title = "修改第一个"
console.log(list)
// [ {id:0, title:'修改第一个'}, {id:1, title:'列表2'}, {id:2, title:'列表3'} ]
4. 经典场景:用 index 生成唯一 ID
开发中批量造模拟数据、列表数据,基本都是用 Array.from:
javascript
// 生成10条带唯一id的模拟数据
const mockData = Array.from({ length: 10 },
(_, i) => ({
id: i + 1,
// id从1开始自增 status: i % 2, content: `测试内容${i}`
}))
5. Array.from 适用场景
-
批量生成对象、数组等引用类型数组(无数据联动)
-
需要根据索引 index 生成差异化数据
-
类数组转换(arguments、DOM NodeList)
-
生成结构化模拟数据、列表渲染数据源
四、Array.from VS Array.fill 核心区别(重点)
| 对比维度 | Array.fill | Array.from |
|---|---|---|
| 是否修改原数组 | 原地修改原数组 | 返回新数组,不修改原数据 |
| 引用类型处理 | 所有元素共用同一内存地址,改一个全联动 | 每次回调生成新对象,数据完全独立 |
| 索引支持 | 无法获取索引,所有元素完全一致 | 自带 index 参数,可生成差异化数据 |
| 使用场景 | 填充基础固定值(数字、字符串) | 生成结构化、差异化、对象数组数据 |
| 遍历能力 | 可将稀疏数组转为密集数组 | 原生支持遍历映射,功能更强 |
五、开发最佳实践(总结口诀)
-
简单定值填充(数字/字符串) :无脑用
Array.fill,简洁高效 -
对象/数组批量生成 :必须用
Array.from,杜绝数据联动 bug -
需要自增 ID、差异化内容 :只用
Array.from的 index 回调 -
绝对禁止:new Array(n).fill({}) 生成对象数组
六、补充:fill 如何正确生成对象数组?(不推荐)
如果非要用 fill 生成独立对象,必须配合 map 二次遍历,代码冗余,远不如 Array.from 简洁:
// 冗余写法,不推荐 const list = new Array(3).fill(0).map((_, i) => ({ id: i }))
结论:生成结构化数据,优先 Array.from;纯定值填充,优先 fill。
结尾
这两个数组 API 看似简单,但暗藏很多开发坑。掌握两者的本质区别,既能避免线上数据联动 bug,又能简化模拟数据、列表数据源的生成代码,大幅提升开发效率。