下記記事でイミュータブルにオブジェクトを扱うことについて触れていますが、その前提としてオブジェクトは参照型のデータ構造であるなどの説明が省かれているので今回その補足です。
※kintone JavaScriptカスタマイズを初学者に教えるにあたって理解してもらいたいポイントなので参照先として記載します。
1. JavaScriptにおけるレコードの表現
kintoneのカスタマイズやプラグイン開発では、レコードをJavaScriptのオブジェクトとして扱います。
const record = {
名前: {
value: "山田太郎"
},
年齢: {
value: 30
},
部署: {
value: "営業部"
}
};
2. オブジェクトのコピーにおける注意点
JavaScriptのオブジェクトをコピーする際には、重要な注意点があります。
単純な代入では、同じオブジェクトへの参照が作成されるだけです。これを「浅いコピー」(Shallow copy)と呼びます。
例:
const original = { name: "太郎" };
const copy = original;
copy.name = "花子";
console.log(original.name); // "花子" と表示されます
original
を直接変更していないにもかかわらず、値が変わっています。これが浅いコピーの特性です。
3. kintoneカスタマイズにおける留意事項
kintoneのカスタマイズにおいても、この特性に注意する必要があります。
例:
const updateRecord = (rec) => {
rec.部署.value = "総務部";
};
updateRecord(record);
console.log(record.部署.value); // "総務部" と表示されます
updateRecord
関数内でrec
を変更すると、元のrecord
オブジェクトも変更されます。
3.1 ユーザー定義関数での副作用
自身で定義した関数においても、Shallow copyによる副作用が発生する可能性があります。以下に例を示します:
const formatName = (record) => {
// 意図せずに元のレコードを変更してしまう
record.名前.value = record.名前.value.toUpperCase();
return `${record.名前.value} さん`;
};
const myRecord = {
名前: {
value: "山田太郎"
}
};
console.log(formatName(myRecord)); // "山田太郎 さん" と表示
console.log(myRecord.名前.value); // "山田太郎" ではなく "山田太郎" と表示されてしまう
この例では、formatName
関数内でrecord
オブジェクトを直接変更しているため、元のmyRecord
オブジェクトも変更されてしまいます。これは意図しない副作用を引き起こす可能性があります。
4. 深いコピーの実装方法
オブジェクトを完全に独立してコピーしたい場合は、「深いコピー」(Deep copy)を使用します。
最新のJavaScript環境では、structuredClone()
関数が利用可能です:
const originalRecord = {
名前: {
value: "山田太郎"
},
年齢: {
value: 30
}
};
const deepCopy = structuredClone(originalRecord);
deepCopy.名前.value = "鈴木花子";
console.log(originalRecord.名前.value); // "山田太郎"
console.log(deepCopy.名前.value); // "鈴木花子"
この方法により、元のオブジェクトを保持したまま、コピーしたオブジェクトのみを変更することが可能です。
5. kintoneカスタマイズにおける実践的な使用例
kintoneのカスタマイズでは、以下のように活用できます:
kintone.events.on('app.record.create.submit', (event) => {
const record = event.record;
// レコードの直接変更(浅いコピー)
record.部署.value = record.部署.value + "配属";
return event;
});
元のレコードの状態を保持したい場合:
kintone.events.on('app.record.create.submit', (event) => {
const originalRecord = event.record;
const copiedRecord = structuredClone(originalRecord);
copiedRecord.部署.value = copiedRecord.部署.value + "配属";
// copiedRecordを使用した処理
event.record = copiedRecord;
return event;
});
ユーザー定義関数での副作用を避ける例:
const formatName = (record) => {
// Deep copyを使用して副作用を防ぐ
const copiedRecord = structuredClone(record);
copiedRecord.名前.value = copiedRecord.名前.value.toUpperCase();
return `${copiedRecord.名前.value} さん`;
};
const myRecord = {
名前: {
value: "山田太郎"
}
};
console.log(formatName(myRecord)); // "山田太郎 さん" と表示
console.log(myRecord.名前.value); // "山田太郎" と表示(元のレコードは変更されていない)
まとめ
- kintoneカスタマイズでは、レコードをJavaScriptのオブジェクトとして操作します。
- オブジェクトの単純な代入は、同一オブジェクトへの参照を生成します(浅いコピー)。
- 浅いコピーは、ユーザー定義関数を含む様々な場面で予期せぬ副作用を引き起こす可能性があります。
- オブジェクトを完全に独立してコピーするには、深いコピーを使用します。
- 深いコピーの実装には
structuredClone()
関数が有効です。 - カスタマイズでは、状況に応じて浅いコピーと深いコピーを適切に使い分けることが重要です。
これらの概念を理解し、適切に応用することで、オブジェクトが参照型であるゆえのバグを防ぐことができます。