javascript
30 lines · 6 steps
Recursively freezing a nested object
A small recursive helper walks every property and freezes each object so a whole config tree becomes deeply immutable.
Explained by
highlit
1function deepFreeze(obj) {
2 const propNames = Reflect.ownKeys(obj);
3
4 for (const name of propNames) {
5 const value = obj[name];
6 if ((value && typeof value === 'object') || typeof value === 'function') {
7 deepFreeze(value);
8 }
9 }
10
11 return Object.freeze(obj);
12}
13
14const config = deepFreeze({
15 env: 'production',
16 server: {
17 host: '0.0.0.0',
18 port: 8080,
19 tls: {
20 enabled: true,
21 ciphers: ['TLS_AES_256_GCM_SHA384'],
22 },
23 },
24 featureFlags: {
25 betaSearch: false,
26 darkMode: true,
27 },
28});
29
30export default config;
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Object.freeze is shallow, so nested objects need recursion to become truly immutable.
- 2Reflect.ownKeys captures string and symbol keys, including non-enumerable ones, unlike a plain for...in.
- 3Freezing children before the parent guarantees the entire tree is locked by the time the top-level call returns.
Related explainers
python
from copy import deepcopy from typing import Any, Mapping
How a recursive deep merge works in Python
recursion
immutability
dictionaries
Intermediate
6 steps
javascript
import { useEffect, useRef } from "react"; import { useBlocker } from "react-router-dom"; export function useUnsavedChangesPrompt(isDirty, message = "You have unsaved changes. Leave anyway?") {
Guarding unsaved changes with a React hook
custom-hooks
navigation-guard
event-listeners
Intermediate
7 steps
ruby
class QueryProxy ALLOWED = %i[where limit offset order includes distinct].freeze def initialize(relation, operations = [])
Building a lazy query proxy in Ruby
metaprogramming
method_missing
lazy-evaluation
Advanced
7 steps
javascript
class InfiniteScroll { constructor(sentinel, { loadMore, root = null, rootMargin = '200px' } = {}) { this.sentinel = sentinel; this.loadMore = loadMore;
Infinite scroll with IntersectionObserver
intersectionobserver
pagination
async
Intermediate
10 steps
php
final class Route { private string $regex; private array $paramNames = [];
Compiling route patterns into regex in PHP
routing
regex
parsing
Intermediate
8 steps
javascript
import { forwardRef, useImperativeHandle, useRef, useState } from 'react'; const TextField = forwardRef(function TextField( { label, error, onChange, ...props },
Exposing an imperative handle in React
forwardref
useimperativehandle
refs
Advanced
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/recursively-freezing-a-nested-object-explained-javascript-b031/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.