javascriptperformanceprototypeprototype-chain

Why is mutating the [[prototype]] of an object bad for performance?


From the MDN docs for the standard setPrototypeOf function as well as the non-standard __proto__ property:

Mutating the [[Prototype]] of an object, no matter how this is accomplished, is strongly discouraged, because it is very slow and unavoidably slows down subsequent execution in modern JavaScript implementations.

Using Function.prototype to add properties is the way to add member functions to javascript classes. Then as the following shows:

function Foo(){}
function bar(){}

var foo = new Foo();

// This is bad: 
//foo.__proto__.bar = bar;

// But this is okay
Foo.prototype.bar = bar;

// Both cause this to be true: 
console.log(foo.__proto__.bar == bar); // true

Why is foo.__proto__.bar = bar; bad? If its bad isn't Foo.prototype.bar = bar; just as bad?

Then why this warning: it is very slow and unavoidably slows down subsequent execution in modern JavaScript implementations. Surely Foo.prototype.bar = bar; is not that bad.

Update Perhaps by mutation they meant reassignment. See accepted answer.


Solution

  • // This is bad: 
    //foo.__proto__.bar = bar;
    
    // But this is okay
    Foo.prototype.bar = bar;
    

    No. Both are doing the same thing (as foo.__proto__ === Foo.prototype), and both are fine. They're just creating a bar property on the Object.getPrototypeOf(foo) object.

    What the statement refers to is assigning to the __proto__ property itself:

    function Employee() {}
    var fred = new Employee();
    
    // Assign a new object to __proto__
    fred.__proto__ = Object.prototype;
    // Or equally:
    Object.setPrototypeOf(fred, Object.prototype);
    

    The warning at the Object.prototype page goes into more detail:

    Mutating the [[Prototype]] of an object is, by the nature of how modern JavaScript engines optimize property accesses, a very slow operation

    They simply state that changing the prototype chain of an already existing object kills optimisations. Instead, you're supposed to create a new object with a different prototype chain via Object.create().

    I couldn't find an explicit reference, but if we consider how V8's hidden classes were implemented (and the more recent write-up), we can see what might go on here. When changing the prototype chain of an object, its internal type changes - it does not simply become a subclass like when adding a property, but is completely swapped. It means that all property lookup optimisations are flushed, and precompiled code will need to be discarded. Or it simply falls back to non-optimized code.

    Some notable quotes: