Mastering JavaScript Class Constructors and Instantiation Logic

The constructor method serves as a specialized function within an ES6 class body, executing automatically whenever a new instance is generated. If this method is absent, the compiler implicitly treats it as a no-op.

When utilizing the new operator, the interpreter follows a specific lifecycle:

  1. It allocates a new blank object in heap memory.
  2. It links the object’s internal prototype ([[Prototype]]) to the class’s static prototype property.
  3. It binds the this keyword inside the method to refer to the new object.
  4. It executes the statements within the constructor to populate properties.
  5. It returns the object, unless an explicit object reference is returned; otherwise, the implicitly created object defaults to the result.

Example showing basic setup:

class Employee {
  constructor(role) {
    console.log('role assigned:', role);
  }
}

class Equipment {
  constructor(condition) {
    this.status = condition || 'neutral';
  }
}

let staff = new Employee('Dev');
let gear = new Equipment('working');

console.log(gear.status); // working

Parameters provided during instantiation are mapped to constructor arguments. Omitting parentheses is permissible if no arguments are required, though including them is harmless.

class Account {
  constructor(owner) {
    this.name = owner || 'Unknown';
  }
}

let a1 = new Account;      // Arguments undefined
let a2 = new Account();    // Arguments undefined
let a3 = new Account('Alex'); // Argument present

console.log(a1.name); // Unknown
console.log(a3.name); // Alex

A constructor implicitly returns this. If the function returns another distinct object, that object overrides the result, but it will lack the prototype linkage. Consequently, the instanceof operator will report false for the original class because the returned object does not inherit from the class prototype.

class Entity {
  constructor(mode) {
    this.primary = 'default';
    if (mode) {
      return { secondary: 'custom' };
    }
  }
}

let e1 = new Entity(false);
let e2 = new Entity(true);

console.log(e1 instanceof Entity); // true
console.log(e2 instanceof Entity); // false

Crucially, class constructors cannot be invoked without the new operator. Calling them as plain functions raises a TypeError. Unlike traditional function constructors, which default this to the global scope in sloppy mode, classes enforce strict binding requirements.

function Legacy() { this.data = true; }
class Modern {}

Legacy();       // Works, attaches to global/window
Modern();       // Throws TypeError

Invoking the constructor reference directly on an instance fails due to binding rules, but the reference can be used to spawn new instances successfully.

let p1 = new Employee('Manager');
p1.constructor(); // Invalid
let p2 = new p1.constructor('Lead'); // Valid

From the engine's perspective, class declarations are essentially functions. Their type check confirms this, and they expose a prototype object containing a constructor property pointing back to the class itself.

console.log(typeof Modern); // function
console.log(Modern === Modern.prototype.constructor); // true

The instanceof operator verifies the existence of the prototype chain. While the standard check works, comparing an instance against the class's constructor property yields different results depending on how the object was initialized.

let obj = new Modern();
console.log(obj.constructor === Modern); // true
console.log(obj instanceof Modern);      // true
console.log(obj instanceof Modern.constructor); // false

Since classes are first-class values, they can be stored in arrays and passed as arguments, enabling flexible factory patterns.

const pool = [
  class Service { constructor(id) { this.id_ = id; } }
];

function generate(cls, val) {
  return new cls(val);
}

generate(pool[0], 99);

Additionally, classes support immediate execution expressions similar to IIFEs, allowing inline instance creation with out naming the class separately.

let res = new class Wrapper {
  setup(value) { this.value = value; }
}(55);

console.log(res.value); // 55

Tags: javascript es6-syntax Prototypes instantiation

Posted on Thu, 09 Jul 2026 17:50:37 +0000 by kpasiva