structuredClone vs Deep Clone
intermediateWhen to use the built-in structuredClone, where it fails (functions, prototypes), and how it compares to JSON round-tripping.
Support Python-style negative array indexing like arr[-1] in JavaScript using a Proxy — a practical introduction to Proxy get and set traps.
Negative indexing in arrays is an interesting topic, especially when combined with JavaScript's Proxy object. In JavaScript, arrays do not support negative indexing natively, but we can simulate this behavior using Proxies.
Let's dive into how to implement negative indexing in arrays using Proxies.
Negative indexing allows accessing array elements from the end of the array, where -1 refers to the last element, -2 to the second last element, and so on. JavaScript arrays do not support negative indexing directly, but we can emulate this feature using JavaScript Proxies.
JavaScript Proxies provide a way to define custom behavior for fundamental operations on objects (e.g., property lookup, assignment, enumeration). They can be used to create an object that wraps another object, allowing for custom behavior.
Interviewers might ask you to:
To implement negative indexing using Proxies, follow these steps:
Here’s how you can achieve negative indexing with Proxies:
function createNegativeIndexingArray(arr) {
return new Proxy(arr, {
get(target, prop) {
const index = Number(prop);
if (Number.isInteger(index)) {
if (index < 0) {
// Convert negative index to positive index
return target[target.length + index];
}
return target[index];
}
return target[prop];
},
set(target, prop, value) {
const index = Number(prop);
if (Number.isInteger(index)) {
if (index < 0) {
// Convert negative index to positive index
target[target.length + index] = value;
} else {
target[index] = value;
}
return true;
}
target[prop] = value;
return true;
}
});
}get Trap: Intercepts property access. If the property is a negative index, it converts it to a positive index and returns the corresponding array element.set Trap: Intercepts property assignment. It converts negative indices to positive indices before setting the value in the array.const arr = createNegativeIndexingArray([10, 20, 30, 40, 50]);
console.log(arr[-1]); // Output: 50
console.log(arr[-2]); // Output: 40
console.log(arr[0]); // Output: 10
arr[-3] = 100;
console.log(arr); // Output: [10, 20, 100, 40, 50]In this example:
createNegativeIndexingArray function wraps the array in a Proxy that supports negative indexing.-1 and -2 are used to access elements from the end of the array.To handle more complex scenarios, such as setting values with out-of-bounds negative indices or accessing non-integer properties, we can extend our Proxy handler:
function createNegativeIndexingArray(arr) {
return new Proxy(arr, {
get(target, prop) {
const index = Number(prop);
if (Number.isInteger(index)) {
if (index < 0) {
const positiveIndex = target.length + index;
return positiveIndex >= 0 ? target[positiveIndex] : undefined;
}
return index < target.length ? target[index] : undefined;
}
return target[prop];
},
set(target, prop, value) {
const index = Number(prop);
if (Number.isInteger(index)) {
if (index < 0) {
const positiveIndex = target.length + index;
if (positiveIndex >= 0) {
target[positiveIndex] = value;
}
} else {
if (index < target.length) {
target[index] = value;
}
}
return true;
}
target[prop] = value;
return true;
}
});
}Challenge: Modify the Proxy handler to return a default value when accessing an index that is out of bounds.
function createNegativeIndexingArray(arr, defaultValue = null) {
return new Proxy(arr, {
get(target, prop) {
const index = Number(prop);
if (Number.isInteger(index)) {
if (index < 0) {
const positiveIndex = target.length + index;
return positiveIndex >= 0 ? target[positiveIndex] : defaultValue;
}
return index < target.length ? target[index] : defaultValue;
}
return target[prop];
},
set(target, prop, value) {
const index = Number(prop);
if (Number.isInteger(index)) {
if (index < 0) {
const positiveIndex = target.length + index;
if (positiveIndex >= 0) {
target[positiveIndex] = value;
}
} else {
if (index < target.length) {
target[index] = value;
}
}
return true;
}
target[prop] = value;
return true;
}
});
}
// Example usage with default values
const arr = createNegativeIndexingArray([10, 20, 30], 'N/A');
console.log(arr[-1]); // Output: 30
console.log(arr[-4]); // Output: N/AIn this challenge:
defaultValue for out-of-bounds accesses, providing a more flexible array implementation.When to use the built-in structuredClone, where it fails (functions, prototypes), and how it compares to JSON round-tripping.
Recursively clone nested objects and arrays, handling cycles with a WeakMap.
Structural equality for nested data: type checks, key comparison, and recursion done right.
Flatten arbitrarily nested arrays recursively and iteratively — then compare with Array.prototype.flat.