Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Enforcing Immutability for JavaScript Constants

Tech 3

Introduction

In JavaScript, the const keyword, introduced in ES6, is used to declare constants. A constant's immutability refers to the inability to reassign the variable's memory address. For primitive data types (like numbers, strings, booleans), this means the value itself cannot change. However, for reference types such as objects and arrays, the reference (the memory address) is fixed, but the contents of the referenced data structure can still be modified. This can make constants appear mutable.

The "Mutable" Constant

Memory in JavaScript is divided into the stack and the heap. Primitive values are stored directly on the stack. Reference types are stored on the heap, while a reference pointer (the memory address) to that heap location is stored on the stack.

When you declare a constant object, you are only preventing the reassignment of that reference pointer. The object's properties remain editable.

const userProfile = {
    username: 'Neo'
}

userProfile.username = 'Morpheus' // This is allowed
console.log(userProfile) // Output: { username: 'Morpheus' }

Freezing Objects

To make an object truly immutable, you can use Object.freeze(). This method transforms an object so that you cannot add new properties, delete existing ones, or modify the values of existing properties.

const gameSettings = {
  difficulty: 'Hard',
  graphics: {
    quality: 'High',
    resolution: '4K'
  }
}

// Shallow freeze
Object.freeze(gameSettings)

gameSettings.difficulty = 'Easy' // This change will be ignored in strict mode or silently fail
console.log(gameSettings.difficulty) // Output: 'Hard'

// Nested objects are not frozen
gameSettings.graphics.quality = 'Medium'
console.log(gameSettings.graphics) // Output: { quality: 'Medium', resolution: '4K' }

As shown, Object.freeze() performs a shallow freeze. It does not affect nested objects. To achieve deep immutability, a recursive freezing function is requirde.

Implementing Deep Freeze

Here is an example of a function that recursively freezes an object and all its nested properties.

function deepFreeze(targetObject) {
  // Retrieve the property names defined on the object itself
  const propNames = Object.getOwnPropertyNames(targetObject)

  // Freeze properties before freezing self
  for (const name of propNames) {
    const value = targetObject[name]
    // Recursively call deepFreeze if the property is an object and not already frozen
    if (value && typeof value === 'object' && !Object.isFrozen(value)) {
      deepFreeze(value)
    }
  }
  // Freeze the initial object
  return Object.freeze(targetObject)
}

const appConfig = {
  theme: 'Dark',
  preferences: {
    notifications: true,
    layout: {
      sidebar: 'Collapsed'
    }
  }
}

deepFreeze(appConfig)

appConfig.theme = 'Light' // Fails silently or throws in strict mode
appConfig.preferences.layout.sidebar = 'Expanded' // Also fails
console.log(appConfig.preferences.layout.sidebar) // Output: 'Collapsed'

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.