Skip to main content

Atoms Overview

Atoms are an important concept in QuickJS for efficient string handling, particularly for property names and identifiers. Understanding atoms is crucial for writing efficient C extensions.

What Are Atoms?

An atom in QuickJS is an interned string identifier represented by a 32-bit integer (JSAtom). Atoms are used throughout the QuickJS engine to represent:
  • Object property names
  • Variable identifiers
  • Function names
  • Symbol descriptions
By using atoms instead of raw strings, QuickJS can perform fast pointer or integer comparisons instead of expensive string comparisons.

Type Definition

typedef uint32_t JSAtom;
Atoms are simple 32-bit unsigned integers that serve as handles to interned strings.

Why Use Atoms?

1. Performance

String comparison is an O(n) operation where n is the length of the string. Atom comparison is O(1) since it’s just an integer comparison.
// Slow: comparing strings
if (strcmp(str1, str2) == 0) { ... }

// Fast: comparing atoms
if (atom1 == atom2) { ... }

2. Memory Efficiency

When the same string is used multiple times (like property names), atoms ensure only one copy exists in memory. All references use the same atom ID.
// In JavaScript, "name" appears many times
let obj1 = { name: "Alice" };
let obj2 = { name: "Bob" };
let obj3 = { name: "Charlie" };

// In memory, "name" is stored once as an atom
// All three objects reference the same atom ID

3. Standardization

The QuickJS API requires atoms for property access functions, making them a standard part of the API.

Null Atom

The special value JS_ATOM_NULL represents an invalid or uninitialized atom:
#define JS_ATOM_NULL 0

Common Use Cases

Object Property Access

Atoms are primarily used when working with object properties:
// Get a property using an atom
JSAtom name_atom = JS_NewAtom(ctx, "name");
JSValue name = JS_GetProperty(ctx, obj, name_atom);
JS_FreeAtom(ctx, name_atom);

Property Definition

JSAtom prop_atom = JS_NewAtom(ctx, "myProperty");
JS_DefinePropertyValue(ctx, obj, prop_atom,
    JS_NewInt32(ctx, 42),
    JS_PROP_C_W_E);
JS_FreeAtom(ctx, prop_atom);

Repeated Property Access

If you access the same property multiple times, create the atom once and reuse it:
// Efficient: create atom once
JSAtom name_atom = JS_NewAtom(ctx, "name");

for (int i = 0; i < count; i++) {
    JSValue name = JS_GetProperty(ctx, objects[i], name_atom);
    // Process name...
    JS_FreeValue(ctx, name);
}

JS_FreeAtom(ctx, name_atom);

Symbol Property Access

Atoms are also used for symbol-keyed properties:
JSValue symbol = JS_NewSymbol(ctx, "unique", false);
JSAtom symbol_atom = JS_ValueToAtom(ctx, symbol);

JS_SetProperty(ctx, obj, symbol_atom, JS_NewInt32(ctx, 100));

JS_FreeAtom(ctx, symbol_atom);
JS_FreeValue(ctx, symbol);

Atom Lifecycle

Atoms use reference counting for memory management:
  1. Creation: When you create an atom, its reference count is 1
  2. Duplication: JS_DupAtom() increments the reference count
  3. Freeing: JS_FreeAtom() decrements the reference count
  4. Deletion: When the reference count reaches 0, the atom is removed from the intern table
// Create atom (refcount = 1)
JSAtom atom = JS_NewAtom(ctx, "property");

// Duplicate atom (refcount = 2)
JSAtom atom2 = JS_DupAtom(ctx, atom);

// Free first reference (refcount = 1)
JS_FreeAtom(ctx, atom);

// Free second reference (refcount = 0, atom deleted)
JS_FreeAtom(ctx, atom2);

Atoms vs. Strings

AspectAtomsStrings
TypeJSAtom (uint32_t)JSValue
Size4 bytes16+ bytes
ComparisonO(1) integer compareO(n) string compare
Use caseProperty names, identifiersText values, output
LifecycleManual reference countingGarbage collected
CreationJS_NewAtom()JS_NewString()

When to Use Atoms

Use atoms when:
  • Accessing object properties
  • Defining properties on objects
  • Working with property enumeration
  • Comparing identifiers repeatedly
  • Performance is critical
Use strings when:
  • Displaying text to users
  • Returning string values from functions
  • Working with arbitrary text content
  • The value is not used as a property key

Performance Tips

  1. Cache atoms for frequently used property names:
    // At initialization
    static JSAtom name_atom = JS_ATOM_NULL;
    
    void init(JSContext *ctx) {
        name_atom = JS_NewAtom(ctx, "name");
    }
    
    void cleanup(JSContext *ctx) {
        JS_FreeAtom(ctx, name_atom);
    }
    
  2. Convert between atoms and strings only when needed:
    // Efficient
    JSAtom atom = JS_NewAtom(ctx, "key");
    JSValue val = JS_GetProperty(ctx, obj, atom);
    JS_FreeAtom(ctx, atom);
    
    // Inefficient
    JSValue str = JS_NewString(ctx, "key");
    JSAtom atom = JS_ValueToAtom(ctx, str);
    JSValue val = JS_GetProperty(ctx, obj, atom);
    JS_FreeAtom(ctx, atom);
    JS_FreeValue(ctx, str);
    
  3. Use string-based property functions for one-off access:
    // For single access, this is simpler
    JSValue val = JS_GetPropertyStr(ctx, obj, "name");
    

See Also

Build docs developers (and LLMs) love