先记住这个答案
fill(value, start, end) 会修改原数组,把下标从 start 到 end - 1 的位置都设成 value,也就是左闭右开区间。负数参数先加上数组长度换算,换算后超出 0 到 length 的部分会被裁剪到边界。长度为 5 时,start 为 -3 换算成 2,end 为 -1 换算成 4,于是只填下标 2 和 3,下标 4 是终点,不包含在内。省略 start 从 0 开始,省略 end 一直填到末尾;终点不大于起点时什么都不填。
- 区间左闭右开,
end那一项不会被填 - 负数加上
length换算,超出范围就裁剪到边界 - 终点不大于起点时一个都不填
fill([])让每一格指向同一个数组
负数和越界的几种情况
把几种参数组合放在一起跑,规则就很清楚了。
const arr = [0, 0, 0, 0, 0]
console.log(JSON.stringify(arr.fill(1, -3, -1))) // 换算成 fill(1, 2, 4)
console.log(JSON.stringify([0, 0, 0].fill(9, 2, 1))) // 终点小于起点,不填
console.log(JSON.stringify([0, 0, 0].fill(9, -10))) // -10 + 3 = -7,裁剪到 0
console.log(JSON.stringify([0, 0, 0].fill(9, 1, 10))) // 10 超出长度,裁剪到 3查看输出与解释
[0,0,1,1,0]
[0,0,0]
[9,9,9]
[0,9,9]第一行就是题目里的情况:-1 代表的是「最后一项」这个位置,但它作为 end 是不包含的,所以最后一项没被填。想从倒数第三项一直填到最后,写 arr.fill(1, -3),不传 end。
左闭右开的好处是 end - start 正好等于填充的个数,slice、copyWithin、substring 用的都是这套规则。记住一个,其它的就都会了。
容易答错的地方
- 以为
-1作为end会包含最后一项 -1换算成length - 1,它是终点,不包含。这个误解和把slice(0, -1)当成「复制整个数组」是同一个问题,其实slice(0, -1)会去掉最后一项。- 以为
fill返回新数组 fill修改原数组并返回原数组本身。在 React 里对状态数组直接fill,会改掉原状态。需要新数组时写[...arr].fill(v),或者Array.from({ length: n }, () => v)。- 以为
fill能扩展数组 [].fill(0, 0, 5)得到的还是空数组,因为end被裁剪到了length,也就是 0。要指定长度,得先用new Array(5)或Array.from({ length: 5 })把长度撑起来。
面试官还会怎么问?
怎么初始化一个 3 行 4 列、全是 0 的二维数组?
Array.from({ length: 3 }, () => new Array(4).fill(0))。外层用 Array.from 保证每一行是独立的数组,内层的元素是数字,用 fill 没有共享问题。
start 传小数或者字符串会怎样?
会先转成整数,小数向零截断,fill(1, 1.8) 等于 fill(1, 1);字符串 '2' 会被转成数字 2;转换后是 NaN 的按 0 处理。实际代码里不建议依赖这种隐式转换。
类数组对象能用 fill 吗?
可以借用,比如 Array.prototype.fill.call({ length: 3 }, 0),它会按 length 在对象上写下 0、1、2 三个属性,返回的还是那个对象,不是数组。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。