By Rahul — Google Frontend Engineer
The 4 Steps of new
When you write new Foo(), JavaScript does exactly four things:
- Creates a new empty object
{} - Sets the prototype of that object to
Foo.prototype - Calls
Foo()withthisbound to the new object - If
Fooreturns an object, use that. Otherwise return the new object from step 1
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function() {
return 'Hi, I am ' + this.name;
};
const rahul = new Person('Rahul', 30);
// Step 1: obj = {}
// Step 2: obj.__proto__ = Person.prototype
// Step 3: Person.call(obj, 'Rahul', 30) → obj.name = 'Rahul', obj.age = 30
// Step 4: Person returns undefined (not an object), so return objImplementing new Yourself
function myNew(Constructor, ...args) {
// Step 1 & 2: Create object with correct prototype
const obj = Object.create(Constructor.prototype);
// Step 3: Call constructor with new object as this
const result = Constructor.apply(obj, args);
// Step 4: If constructor returned an object, use it
return result instanceof Object ? result : obj;
}
const p = myNew(Person, 'Rahul', 30);
p.greet(); // "Hi, I am Rahul"
p instanceof Person; // trueThe Return Value Trap
function Weird() {
this.name = 'internal';
return { name: 'external' }; // Returns an object!
}
const w = new Weird();
console.log(w.name); // "external" — the returned object wins
console.log(w instanceof Weird); // false!
function Normal() {
this.name = 'internal';
return 42; // Returns a primitive — ignored
}
const n = new Normal();
console.log(n.name); // "internal" — primitive return is ignoredForgetting new
function User(name) {
this.name = name;
}
// Without new, this === globalThis (or undefined in strict mode)
const u = User('Rahul');
console.log(u); // undefined
console.log(globalThis.name); // "Rahul" — polluted global!
// Protection pattern
function SafeUser(name) {
if (!(this instanceof SafeUser)) {
return new SafeUser(name);
}
this.name = name;
}
// Modern protection — ES6 classes throw automatically
class ModernUser {
constructor(name) {
this.name = name;
}
}
ModernUser('Rahul'); // TypeError: must be called with newnew with Arrow Functions
const Foo = () => {};
new Foo(); // TypeError: Foo is not a constructor
// Arrow functions do not have [[Construct]]
// They cannot be used with newBest Practices
- Use ES6 classes instead of constructor functions — they enforce
newautomatically - If you must use constructor functions, add the
instanceofcheck - Capitalize constructor function names (convention that signals "use new")
- Never return objects from constructors unless you have a specific reason
Summary
The new operator creates an object, links its prototype, calls the constructor, and handles the return value. Understanding these 4 steps is fundamental to understanding JavaScript's object model.