Array.prototype.with()
Array.prototype.with() in ECMAScript 14
ECMAScript 14 introduces the Array.prototype.with()
method, which brings new possibilities to array operations. This method allows developers to modify specific elements of an array in an immutable way, returning a new array without altering the original array.
Basic Usage of Array.prototype.with()
The with()
method takes two parameters: the index to modify and the new value. It returns a new array where the element at the specified index is replaced with the new value, while the original array remains unchanged.
const originalArray = [1, 2, 3, 4, 5];
const newArray = originalArray.with(2, 10);
console.log(originalArray); // [1, 2, 3, 4, 5]
console.log(newArray); // [1, 2, 10, 4, 5]
Difference from Direct Assignment
The traditional way to modify an array is through direct assignment by index, which changes the original array:
const array = [1, 2, 3];
array[1] = 4;
console.log(array); // [1, 4, 3]
In contrast, the with()
method preserves the immutability of the original array:
const array = [1, 2, 3];
const newArray = array.with(1, 4);
console.log(array); // [1, 2, 3]
console.log(newArray); // [1, 4, 3]
Handling Negative Indices
The with()
method supports negative indices, where -1 represents the last element, -2 the second-to-last element, and so on:
const colors = ['red', 'green', 'blue'];
const newColors = colors.with(-1, 'yellow');
console.log(newColors); // ['red', 'green', 'yellow']
Edge Case Handling
When the index is out of the array's bounds, with()
throws a RangeError:
try {
const arr = [1, 2, 3];
arr.with(5, 10); // Throws RangeError
} catch (error) {
console.error(error); // RangeError: Invalid index
}
Comparison with the Spread Operator
The spread operator can achieve a similar effect, but the syntax is more verbose:
const original = [1, 2, 3, 4];
const index = 2;
const value = 10;
// Using the spread operator
const newArray = [...original.slice(0, index), value, ...original.slice(index + 1)];
// Using with()
const withArray = original.with(index, value);
Performance Considerations
Although the with()
method creates a new array, modern JavaScript engines optimize this operation. For large arrays, engines like V8 use techniques such as structural sharing to improve performance.
Practical Use Cases
- State Management: Maintaining immutability in Redux or React state updates
function updateItem(state, index, newValue) {
return {
...state,
items: state.items.with(index, newValue)
};
}
- Undo/Redo Functionality: Maintaining history without modifying the original array
const history = [];
let currentState = [1, 2, 3];
function updateState(index, value) {
history.push(currentState);
currentState = currentState.with(index, value);
}
- Functional Programming: Using with other array methods
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
const updatedUsers = users
.map(user => user.id === 2 ? {...user, name: 'Robert'} : user)
// Equivalent to
.with(1, {...users[1], name: 'Robert'});
Combining with Other Array Methods
with()
can be chained with other array methods:
const numbers = [1, 2, 3, 4, 5];
const result = numbers
.filter(n => n % 2 === 0)
.with(0, 10)
.map(n => n * 2);
console.log(result); // [20, 8]
Use in Type Systems
In TypeScript, with()
maintains type safety for arrays:
const strings: string[] = ['a', 'b', 'c'];
const updated = strings.with(1, 'd'); // Type remains string[]
// Type error example
// strings.with(1, 123); // Error: Cannot assign number to string
Browser Compatibility and Polyfill
Most modern browsers now support Array.prototype.with()
. For older environments, a polyfill can be used:
if (!Array.prototype.with) {
Array.prototype.with = function(index, value) {
if (index < -this.length || index >= this.length) {
throw new RangeError('Invalid index');
}
const actualIndex = index < 0 ? this.length + index : index;
const copy = [...this];
copy[actualIndex] = value;
return copy;
};
}
Comparison with Similar Methods
Method | Modifies Original | Return Value | Use Case |
---|---|---|---|
with() | No | New array | Immutable updates |
splice() | Yes | Removed items | When modifying original |
fill() | Yes/No (with copy) | Modified array | Batch value filling |
Spread operator | No | New array | For complex operations |
Usage in React
In React components, with()
can simplify state updates:
function TodoList() {
const [todos, setTodos] = useState([
{ text: 'Learn React', completed: false },
{ text: 'Build app', completed: false }
]);
const toggleTodo = (index) => {
setTodos(todos.with(index, {
...todos[index],
completed: !todos[index].completed
}));
};
// ...
}
Handling Multidimensional Arrays
For multidimensional arrays, with()
can be nested:
const matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
const updatedMatrix = matrix.with(1, matrix[1].with(2, 10));
// [
// [1, 2, 3],
// [4, 5, 10],
// [7, 8, 9]
// ]
Combining with Object.freeze()
with()
works particularly well with Object.freeze()
to create truly immutable data structures:
const frozenArray = Object.freeze([1, 2, 3]);
// frozenArray[1] = 4; // Throws an error in strict mode
const updated = frozenArray.with(1, 4); // This is allowed
Common Pitfalls
-
Assuming
with()
modifies the original array:const arr = [1, 2, 3]; arr.with(1, 4); // No effect, must capture the return value
-
Ignoring the return value:
// Incorrect usage array.with(index, value); // Correct usage const newArray = array.with(index, value);
-
Confusing with
at()
method:// at() retrieves an element array.at(-1); // with() replaces an element array.with(-1, value);
Performance Testing Example
Comparing the performance of with()
and the spread operator:
const largeArray = Array(10000).fill(0);
console.time('with');
for (let i = 0; i < 1000; i++) {
largeArray.with(5000, 1);
}
console.timeEnd('with');
console.time('spread');
for (let i = 0; i < 1000; i++) {
[...largeArray.slice(0, 5000), 1, ...largeArray.slice(5001)];
}
console.timeEnd('spread');
Application on Array-like Objects
Although with()
is an Array method, it can be used on array-like objects via Array.from()
or the spread operator:
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
const realArray = Array.from(arrayLike);
const updated = realArray.with(1, 'x');
Integration with Other ECMAScript 14 Features
with()
can be combined with the Record and Tuple proposal (if implemented) to create fully immutable data structures:
// Assuming Record and Tuple are implemented
const record = #{ items: #[1, 2, 3] };
const newRecord = #{ ...record, items: record.items.with(1, 4) };
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:Array.prototype.toSpliced()
下一篇:结构化克隆算法改进