先记住这个答案
fill(value, start, end) 修改原数组中起点包含、终点不包含的索引范围。负数位置按 length 加该值换算,再按合法范围裁剪;省略 start 从零开始,省略 end 到长度结束。长度五时 start 为 -3 对应索引二,end 为 -1 对应索引四,因此只填二、三,不包含最后的四。如果终点不大于起点,就不填任何位置。fill 不扩展数组长度,并把同一个 value 放到每个位置;value 是对象时不会为每格创建独立对象。
- 负索引先加长度再裁剪到边界
- end 指向的索引不参与填充
- 返回原数组且每格共享传入的对象值
明确最后一个参数是终点而非数量
下面数组长度五,负三和负一分别换算成二和四,所以两个中间位置被替换。end 不是填多少项,也不是包含式终点;如果想填到最后,应省略 end 或传数组长度。
为了便于检查,示例同时比较返回值身份和原数组内容。只看返回的打印结果容易误以为得到了新数组,实际调用方和其他共享引用看到的是同一份修改后的容器。
const values = ['A', 'B', 'C', 'D', 'E'];
const returned = values.fill('X', -3, -1);
console.log(JSON.stringify(values));
console.log(returned === values);
console.log(JSON.stringify([1, 2, 3].fill(9, -99, 1)));
console.log(JSON.stringify([1, 2, 3].fill(9, 2, 1)));查看输出与解释
["A","B","X","X","E"]
true
[9,2,3]
[1,2,3]过小的负起点被裁剪到零;终点在起点之前时区间为空,数组保持不变。所有例子都在既有长度范围内赋值,不会因越界参数自动增加元素数量。
填充对象时确认是否需要独立实例
用 Array(3).fill({ selected: false }) 创建三行状态时,三格引用同一对象,修改一格的属性会让其他格看到变化。fill 接收的是一个已经求值的 value,而不是每格调用一次的工厂。
需要独立对象时可以用 Array.from 的映射函数逐项创建,或者在已填充的数组上 map 返回新对象。若数据来自只读状态,先决定容器和元素哪一层需要复制,再执行局部填充。
区间为空与数组为空都不会生成新容量
空数组的长度是零,直接 [].fill(0) 仍为空。要创建固定长度内容,应先建立对应长度或使用生成方式,不能指望 fill 根据起止参数自行扩容。
fill 可以给稀疏数组的缺失索引写入真实属性,但它不是任意数据修复器。只读属性、冻结对象或代理拦截可能导致失败,业务层仍应确认目标是允许修改的容器。
容易答错的地方
- 把 end 为 -1 理解成包含最后一项
- 负一换算后指向最后索引,但终点本来就不包含,所以最后一项不会写入。应先做索引换算再应用区间规则,避免把自然语言“到最后”与 API 的边界契约混在一起。
- 用 fill 创建多份独立对象
- 传给 fill 的对象只创建了一次,每次写入都是同一个引用。若每格需要独立状态,应使用逐项工厂,并通过对象身份和单格修改测试确认没有意外共享。
面试官还会怎么问?
start 超过数组长度会扩展数组吗?
不会,归一化后的范围没有可填位置,长度保持不变。fill 的职责是修改既有长度范围内的元素,若需求是追加内容,应选择追加或重新构造数组的操作。
fill 会保留空位吗?
被覆盖范围内的缺失索引会写成真实属性,范围外的空位保持原状。因此填充后可能只有一部分变成密集结构,后续 map 是否执行仍应按具体索引存在性判断。
给响应式状态调用 fill 有什么注意点?
首先它修改原容器,是否触发更新取决于所用状态系统的契约;其次对象 value 仍然共享引用。应同时验证框架更新机制和数据身份,不能只因为最终打印值正确就忽略状态传播问题。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。