この章では、データの更新方法を 2 つ学習します。
後者は、最初にコピーを作成してから破壊的に変更するのと似ていますが、同時に両方を行います。
次のコードは、オブジェクトのプロパティを破壊的に更新する関数と、オブジェクトに対して関数を使用する方法を示しています。
function setPropertyDestructively(obj, key, value) {
obj[key] = value;
return obj;
}
const obj = {city: 'Berlin', country: 'Germany'};
setPropertyDestructively(obj, 'city', 'Munich');
assert.deepEqual(obj, {city: 'Munich', country: 'Germany'});
次のコードは、オブジェクトの非破壊的更新を示しています。
function setPropertyNonDestructively(obj, key, value) {
const updatedObj = {};
for (const [k, v] of Object.entries(obj)) {
updatedObj[k] = (k === key ? value : v);
}
return updatedObj;
}
const obj = {city: 'Berlin', country: 'Germany'};
const updatedObj = setPropertyNonDestructively(obj, 'city', 'Munich');
// We have created an updated object:
assert.deepEqual(updatedObj, {city: 'Munich', country: 'Germany'});
// But we didn’t change the original:
assert.deepEqual(obj, {city: 'Berlin', country: 'Germany'});
スプレッド演算子を使用すると、setPropertyNonDestructively()
がより簡潔になります。
setPropertyNonDestructively()
の両方のバージョンは 浅く 更新します。つまり、オブジェクトの最上位レベルのみを変更します。
次のコードは、配列の要素を破壊的に更新する関数と、配列に対して関数を使用する方法を示しています。
function setElementDestructively(arr, index, value) {
arr[index] = value;
}
const arr = ['a', 'b', 'c', 'd', 'e'];
setElementDestructively(arr, 2, 'x');
assert.deepEqual(arr, ['a', 'b', 'x', 'd', 'e']);
次のコードは、配列の非破壊的更新を示しています。
function setElementNonDestructively(arr, index, value) {
const updatedArr = [];
for (const [i, v] of arr.entries()) {
updatedArr.push(i === index ? value : v);
}
return updatedArr;
}
const arr = ['a', 'b', 'c', 'd', 'e'];
const updatedArr = setElementNonDestructively(arr, 2, 'x');
assert.deepEqual(updatedArr, ['a', 'b', 'x', 'd', 'e']);
assert.deepEqual(arr, ['a', 'b', 'c', 'd', 'e']);
.slice()
とスプレッド演算子を使用すると、setElementNonDestructively()
がより簡潔になります。
function setElementNonDestructively(arr, index, value) {
return [
...arr.slice(0, index), value, ...arr.slice(index+1)];
}
setElementNonDestructively()
の両方のバージョンは 浅く 更新します。つまり、配列の最上位レベルのみを変更します。
これまでのところ、データを浅くしか更新していません。ディープ更新に取り組みましょう。次のコードは、手動でそれを行う方法を示しています。name と employer を変更しています。
const original = {name: 'Jane', work: {employer: 'Acme'}};
const updatedOriginal = {
...original,
name: 'John',
work: {
...original.work,
employer: 'Spectre'
},
};
assert.deepEqual(
original, {name: 'Jane', work: {employer: 'Acme'}});
assert.deepEqual(
updatedOriginal, {name: 'John', work: {employer: 'Spectre'}});
次の関数は、一般的なディープ更新を実装します。
function deepUpdate(original, keys, value) {
if (keys.length === 0) {
return value;
}
const currentKey = keys[0];
if (Array.isArray(original)) {
return original.map(
(v, index) => index === currentKey
? deepUpdate(v, keys.slice(1), value) // (A)
: v); // (B)
} else if (typeof original === 'object' && original !== null) {
return Object.fromEntries(
Object.entries(original).map(
(keyValuePair) => {
const [k,v] = keyValuePair;
if (k === currentKey) {
return [k, deepUpdate(v, keys.slice(1), value)]; // (C)
} else {
return keyValuePair; // (D)
}
}));
} else {
// Primitive value
return original;
}
}
value
を更新するツリーのルートと見なした場合、deepUpdate()
は単一のブランチ (A 行と C 行) のみをディープに変更します。他のすべてのブランチは浅くコピーされます (B 行と D 行)。
deepUpdate()
の使用方法は次のとおりです。