Array.prototype.with 是 ES2023 新增的不可变数组方法,用于 O(1) 安全替换指定索引元素并自动处理边界,比 slice+concat 或展开运算符更简洁可靠,天然适配 React 纯函数状态更新。

Array.prototype.with 是 ECMAScript 2023(ES14)新增的数组方法,用于**安全、不可变地替换指定索引处的元素**,返回一个新数组,不修改原数组。它天然契合 React 中“纯函数更新状态”的理念,能替代手写 slice + concat 或展开运算符拼接等易出错、可读性差的写法,提升状态更新的准确性与性能可预测性。
为什么 with 比传统方式更适合 React 状态更新
在 React 中,直接修改数组(如 arr[i] = newItem)会破坏响应式追踪,且违背不可变原则;而常见替代方案存在冗余或边界风险:
- slice + 替换 + concat:需手动拆分三段,索引越界时逻辑易错(如 i === 0 或 i === arr.length - 1)
- [...arr.slice(0, i), newItem, ...arr.slice(i + 1)]:创建多个中间数组,内存开销略高,且对稀疏数组或大数组不够友好
- map 遍历全量:O(n) 时间复杂度,即使只改一项,对长列表不必要
with(i, newItem) 是 O(1) 级别的语义化操作:内部高效构造新数组,自动处理边界(如负索引、越界),代码意图一目了然。
在 useState / useReducer 中直接使用 with 更新数组状态
无需封装工具函数,直接链式调用即可。配合函数式 setState,确保更新逻辑纯净:
const [items, setItems] = useState(['a', 'b', 'c']); <p>// ✅ 推荐:用 with 替换第 1 项('b' → 'B') setItems(prev => prev.with(1, 'B'));</p><p>// ✅ 支持负索引(-1 表示最后一项) setItems(prev => prev.with(-1, 'C'));</p><p>// ✅ 安全越界:索引超出范围时,with 返回原数组(不报错) setItems(prev => prev.with(100, 'X')); // 仍为 ['a', 'b', 'c']
结合 map 或 filter 实现复合更新(保持不可变性)
with 本身只改单个元素,但可与其他数组方法组合,构建声明式更新链:
Orderly React SDK 钩子使用参考指南,包括 useOrderEntry、usePositionStream、useOrderbookStream、useCollateral 等。
- 先 filter 再 with:例如“移除某类项后,更新剩余列表中第一项”
- 先 map 再 with:例如“统一转换所有值后再修正某一项”
- 避免嵌套深、副作用多的 for 循环或 splice
示例:更新满足条件的第一个对象的 status 字段
setItems(prev =>
prev
.map((item, i) => (item.id === targetId ? { ...item, status: 'done' } : item))
.with(
prev.findIndex(item => item.id === targetId),
{ ...prev.find(item => item.id === targetId), status: 'done' }
)
);
更简洁写法(推荐):
const idx = items.findIndex(item => item.id === targetId);
if (idx !== -1) {
setItems(prev => prev.with(idx, { ...prev[idx], status: 'done' }));
}
注意事项与兼容性兜底
目前(2024 年中)with 方法尚未被所有浏览器/环境完全支持(如旧版 Safari、Node.js < 20.7)。生产环境需谨慎:
- 检查是否存在:
if ('with' in Array.prototype) - 按需 polyfill(可用 core-js 或自行实现轻量版)
- React 项目中若已用 babel-preset-react-app 或 swc,确认构建配置启用 ES2023 提案支持
简易 polyfill 示例(仅覆盖常用场景):
if (!Array.prototype.with) {
Array.prototype.with = function(i, value) {
const len = this.length;
const idx = i < 0 ? len + i : i;
if (idx < 0 || idx >= len) return this;
const copy = Array.from(this);
copy[idx] = value;
return copy;
};
}


















