「オブジェクトと配列を定数で固定してるのに値の更新が行われる」
「Object.freezeまでやっているのに値を固定できていない」
本日はそんな方に向けてObject.freezeの正しい使い方を解説します。
Web制作やWebアプリの開発者にとってオブジェクトの管理は非常に重要です。
特にオブジェクトのプロパティが予期せず変更されることを防ぐ必要がある場合、Object.freeze()というメソッドが非常に役立ちます。
しかし多くの初心者がObject.freeze()を誤解しており、その使い方について混乱していることがあります。
この記事ではJavaScriptのObject.freeze()メソッドに関するよくある勘違いを解消し、正しい使い方を詳しく説明します。
オブジェクトの凍結とは何か、どのように使用するのか、そしてどのような利点があるのか、すべてを明らかにしていきましょう。
JavaScriptのプロジェクトをより安全かつ信頼性の高いものにするために、Object.freeze()をマスターしましょう。
また動画もあるので必要に応じて使ってください。
JavaScriptでオブジェクトと配列の更新を禁止する
まず定数constでオブジェクトと配列を定義した場合に、プロパティについては値の更新ができてしまうのが背景にあります。
「オブジェクトと配列をconstで定義しているのに中身が変わってしまう」という方は、まずそこから知っておく必要があります。
詳細は別記事で既に取り上げているので参考にしてください。
そんな時にJavaScriptではObject.freezeという関数が用意されていて、引数に固定したいオブジェクトもしくは配列を入れることでプロパティの更新を禁止することができます。
// 通常のオブジェクトの書き方
const student = {
id: "0001",
name: "山田",
age: 18,
};
student.id = "0002";
console.log(student.id); // "0002"と更新されて出力されてしまう
// Object.freezeを使った書き方
const student = Object.freeze({
id: "0001",
name: "山田",
age: 18,
});
student.id = "0002";
console.log(student.id); // "0001"のままで出力される
上記のようなシンプルな作りだと困ることはないのですが、以下のような入れ子になったオブジェクトと配列は気を付けないといけません。
const student = Object.freeze({
id: "0001",
name: "山田",
age: 18,
// こちらを追加
address: {
preference: "Tokyo",
block: "Akabane",
},
});
student.address.block = "Setgaya";
console.log(student.address.block); // "Setagaya"と更新されて出力されてしまう
なんとObject.freezeを設定しているのにプロパティの更新が実行されてしまいました。
こちらエラーではなく通常の動作でして、公式ドキュメントにも明記されているんです。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
簡単に言うとObject.freezeは「入れ子の深いところまでは対応できない」とのことです。
そのため入れ子の深い部分まで固定したい場合には、中身でもObject.freezeを使う必要があります。
const student = Object.freeze({
id: "0001",
name: "山田",
age: 18,
// ここでもObject.freezeを使う
address: Object.freeze({
preference: "Tokyo",
block: "Akabane",
}),
});
student.address.block = "Setgaya";
console.log(student.address.block); // "Akabane"のままで出力される
ちなみに値の更新だけでなく、新規追加でも同じことが言えます。
多いケースとして配列のpushがよく使われますね。
const student = Object.freeze({
id: "0001",
name: "山田",
age: 18,
address: Object.freeze({
preference: "Tokyo",
block: "Akabane",
}),
// こちらを追加
circles: ["football", "tennis"],
});
student.circles.push("baseball");
console.log(student.circles); // ["football", "tennis", "baseball"]と出力されてしまう
上記の場合もプロパティにまでObject.freezeを使う必要があります。
const student = Object.freeze({
id: "0001",
name: "山田",
age: 18,
address: Object.freeze({
preference: "Tokyo",
block: "Akabane",
}),
circles: Object.freeze(["football", "tennis"]),
});
student.circles.push("baseball");
console.log(student.circles); // ["football", "tennis"]のまま出力される
APIなど連想配列でデータが構成されることは当たり前になってきましたので、オブジェクトと配列の扱いには注意しましょう。
ちなみにObject.freezeはオブジェクトの宣言の後で使用しても同じような効果が得られます。
先ほどのコードは以下のように書いても同じ意味になります。
const student = {
id: "0001",
name: "山田",
age: 18,
address: {
preference: "Tokyo",
block: "Akabane",
},
circles: ["football", "tennis"],
};
Object.freeze(student);
Object.freeze(student.address);
Object.freeze(student.circles);
定数を宣言するときに書くのか、宣言した後で書くのかは個人の好みだと思います。
どちらの書き方も覚えておくと良いでしょう。