JavaScript Objects Explained: Properties, Ordinary vs. Exotic, and Realms

11 min read, Wed, 21 May 2025

JavaScript Types Image from pixabay.com

This article delves into the details of an object in JavaScript or more technically ECMAScript.

Let’s begin with a fundamental definition: a JavaScript object is essentially a collection of key-value pairs, where keys are typically strings or Symbols. They are fundamental to JavaScript’s data structure model, enabling the creation of custom data structures and more complex organizations of data.

Data Property vs Accessor Property

The keys of an object are also referred to as property name, and each property falls into one of two categories: a data property or an accessor property:

Data Property

A data property holds a JavaScript language value (e.g., undefined, null, string, symbol, object, number, bigint, boolean) along with a set of internal Boolean attributes that define its configuration.

const person = {
    name: "Bram Caro",
    age: 45,
};

In the code example above, we have defined data properties. We have created an object with property keys name and age. These property keys directly hold the value.

You can retrieve the attributes of a data property using the Object.getOwnPropertyDescriptor() method. The example below demonstrates this concept:

console.log(Object.getOwnPropertyDescriptor(person, "name"));

The code above outputs a set of data property attributes:

{
  value: 'Bram Caro',
  writable: true,
  enumerable: true,
  configurable: true
}
Object.defineProperty(person, "name", {
    writable: false,
});

person.name = "Some other name";

console.log(person.name); //Bram Caro

In this example, the name property of the person object is set to writable: false. On the next line, we attempt to change the value of name. This operation will not throw an error but will silently fail. Consequently, console.log(person.name) will still print the original value.

Accessor Property

An accessor property associates a key with one or two accessor functions (a getter, a setter, or both) and a set of internal Boolean attributes. These accessor functions are executed to control how the property’s value is stored and retrieved.

Accessor properties are excellent candidates for performing calculations on properties before retrieving them, executing validations, or triggering side effects.

In the example below, we will demonstrate how to implement accessor property:

const person = {
    personName: "Bram Caro",
    // This is a data property holding the actual name

    // 'name' is an accessor property
    get name() {
        return this.personName; // The getter function
    },

    set name(value) {
        this.personName = value; // The setter function
    },
};

console.log(person.name); // Accesses the getter
person.name = "Amole"; // Accesses the setter
console.log(person.name); // Accesses the getter again

This example defines an accessor property named name on the person object.

{
  get: [Function: get name],
  set: [Function: set name],
  enumerable: true,
  configurable: true
}

This output is from Object.getOwnPropertyDescriptor(person, "name") when applied to an accessor property. Unlike a data property descriptor, it does not have a value or writable attribute. Instead, it has:

Ordinary vs Exotic Objects

JavaScript objects are broadly categorized into two fundamental types:

Ordinary Object

An Ordinary Object is a standard JavaScript object without any unique or ‘magical’ built-in behaviors. They possess all the essential internal slots and methods as defined by the language specification. Examples include objects created via the Object() constructor or using object literal syntax (e.g., {}).

Exotic Objects

An Exotic Object is any object that deviates from the standard behaviors of ordinary objects, possessing unique internal semantics or ‘special’ functionalities. Simply put, it’s an object that is not an ordinary object. A prime example is the JavaScript Array. While arrays are indeed objects, they exhibit special behaviors, such as their length property automatically updating when elements are added or removed. Conversely, if you manually set an array’s length to zero, the array will be truncated. These are the kinds of built-in ‘magical’ functionalities that differentiate Exotic Objects like Arrays from ordinary objects.

These exotic behaviors exist for performance reasons and to enable distinct functionalities for objects. For example, by making Array an exotic object, JavaScript engines can implement ‘Fast Arrays.’ Using 32-bit indices, direct indexing of array elements allows engines like V8 to directly access memory offsets instead of first performing a hash key lookup.

Another example of an exotic object is a function object. ECMAScript allows function objects to be called, to construct new objects, and to set their prototype property for inheritance. Allowing an object to be called and to have instructions execute when called provides the behavior for function execution.

An example of function object:

function CallableObject() {
    console.log("I am a callable object");
}

CallableObject();

This code defines CallableObject, which is a function. When CallableObject() is invoked, the code inside its body (console.log("I am a callable object")) is executed, demonstrating its “callable” exotic behavior.

console.log(Object.getPrototypeOf(CallableObject));
// Output: [Function CallableObject] Object
console.log(CallableObject instanceof Object); // true

These lines confirm that CallableObject is indeed an object.

Another example, using Function constructor:

const fobj = new Function('console.log("I am a callable object two");');
fobj(); // Output: I am a callable object two

Here, fobj is created using the Function constructor. This demonstrates another way to create a function object programmatically. When fobj() is called, the string provided to the constructor is executed as JavaScript code. This highlights the ability of function objects to encapsulate and execute code.

While functions can be created using the Function constructor with the new keyword, doing so is generally discouraged in a production environment. This is primarily due to security risks (as it evaluates strings as code, similar to eval()) and potential performance overhead. The recommended way to define a function is using the function keyword (or arrow function syntax), which provides better readability and is easier to maintain.

Object Indices

The ECMAScript specification formally distinguishes between two types of indices: Integer Index and Array Index.

Integer Index

An Integer Index applies to ordinary objects. In JavaScript, ordinary objects can utilize square bracket notation to access or define properties whose keys are numeric integral values.

const obj = {};

obj[1] = "value for index one";
console.log(obj); // Output: { '1': 'value for index one' }
console.log(obj[1]); // Output: value for index one.

An Integer Index is defined as an integral number (a non-fractional number) ranging from +0 up to 253 −1. Crucially, because JavaScript object keys can only be strings or Symbols, any numeric integral index used as a key on an ordinary object is implicitly converted to its string representation. For example, the numeric index 1 in the previous example is converted to the string ‘1’, which is why the output console.log(obj) shows the key wrapped in single quotes: { '1': 'value for index one' }.

Array Index

An Array Index is a subset of Integer Indices, ranging from +0 up to 232 −2. This specific range is reserved for true array indexing. Consequently, an array (being an exotic object with specialized behavior) is effectively limited to this upper bound for its length and directly accessible elements.

Intrinsic, Fundamental Objects

Beyond the classification of Ordinary and Exotic Objects, the ECMAScript specification further categorizes objects, particularly among its built-in objects. While these categories sometimes overlap, they highlight specific architectural roles.

Intrinsic Objects

In JavaScript (as defined by the ECMAScript specification), Intrinsic Objects are fundamental built-in objects that are automatically initialized by the JavaScript engine (e.g., V8, SpiderMonkey) before any user code begins execution. They form the core runtime environment of the language. Key examples include Object, Array, Function, and Promise.

Crucially, Intrinsic Objects are realm-specific identities: each Realm possesses its own unique set of these foundational objects. These objects are created and fully initialized during the Realm’s instantiation.

What is a Realm?

A Realm is a distinct sandbox environment, or execution context, designed for evaluating ECMAScript (JavaScript) code. In a browser environment, an <iframe> is a prime example of a Realm. JavaScript code executing within an <iframe> operates in its own Realm, possessing a distinct set of Intrinsic Objects (built-in objects like Object, Array, Function, etc.).

A realm example of VM in Node.JS:

const vm = require("vm");

const realm1 = vm.createContext();
const realm2 = vm.createContext();

vm.runInContext("var y='I am in Realm One';", realm1);
vm.runInContext("var y='I am in Realm Two';", realm2);

console.log(realm1.y); // I am in Realm One
console.log(realm2.y); // I am in Realm Two
console.log(y); // Uncaught ReferenceError: y is not defined

In essence, a Realm provides a self-contained execution environment complete with its own set of built-in objects, a unique global scope, and an independent set of global properties. It serves as the fundamental context within which ECMAScript code is evaluated, and where all JavaScript objects are created. Host environments are responsible for establishing and managing these Realms.

Fundamental Objects

Fundamental Objects are those built-in objects that are absolutely essential for the runtime semantics of the ECMAScript language. This category includes, but is not limited to, the Object object, Function object, Boolean object, Symbol object, and various Error objects. A host environment cannot function without the presence and proper operation of these Fundamental Objects.

The primary Fundamental Objects commonly identified are:

  1. Object (the constructor/prototype for generic objects)
  2. Function (the constructor/prototype for functions)
  3. Boolean (the wrapper for boolean primitives)
  4. Symbol (the wrapper for symbol primitives)
  5. Error (and its sub-types like TypeError, RangeError, etc.)

For instance, consider a scenario where the Object Fundamental Object is not present in an execution context; in such a case, the very concept of an object would cease to exist.

Object.prototype serves as the base prototype for virtually all other objects in JavaScript, meaning they inherit properties and methods from it. Consequently, without the Object constructor and its Object.prototype, fundamental language features like object instantiation, property inheritance, and even basic object operations would be impossible.

Summary

In this comprehensive exploration, we’ve dissected the multifaceted nature of JavaScript objects as defined by the ECMAScript specification. From the foundational distinction between data and accessor properties to the nuanced categories of ordinary and exotic objects, and further into the roles of intrinsic and fundamental built-ins and isolated Realms, understanding these concepts is crucial. This deeper knowledge empowers you to write more efficient, predictable, and robust JavaScript code by truly comprehending what happens beneath the surface. As you continue your journey in JavaScript, remember that objects are at the very heart of the language, and mastering their intricacies is key to becoming a proficient developer.