javascriptarraysjavascript-objectsprototype-chain

Can we break the pre-defined prototype chain of Numbers, Strings, Boolean, Arrays in JS Manually or Reassign Values


When, we make a new variable of type number (say num1), its __proto__ points to Number Object. The __proto__ of it, will again point to Object Core, its __proto__ points to null, thereby ending the Prototype-chain.

What I am trying to do, is override this chain in middle, by trying to change the __proto__ link from Number Object to some string. I cannot do the break in chain, as after my assignment its still pointing to Object Core structure.

Am I doing something wrong to break this prototype chain OR is it impossible to break the pre-defined prototype chain in browser!

Number.__proto__ = "abc"; 
let num4 = 8;
console.log(num4.__proto__);//returns Number Object
console.log(num4.__proto__.__proto__)//should return string "abc"

Output:

enter image description here

However, I know I can add certain item in the middle of chain (like in Number-Object) by following code:

Number.prototype.alpha = "def";
let num5 = 99; 
console.log(num5.__proto__);

Output:

enter image description here


Solution

  • Modifying the built-in prototypes of fundamental objects, or the built-in prototype chains, is a very bad idea in the general case. I strongly recommend not doing it.

    Number.__proto__ is the prototype of the Number function, not the prototype for Number objects (which is Number.prototype).

    Also, JavaScript has both number primitives and Number objects. Most of the time, you're dealing with a number primitive. When you use properties (including methods) on a number primitive (n = 42) such as like toString, the JavaScript engine gets those from Number.prototype even though n is a primitive.

    You can change the prototype of Number.prototype. For instance, you can set it to null so that Number objects no longer inherit from anything but Number.prototype (breaking the link with Object.prototype), meaning property lookup on a number primitive doesn't find properties and methods from Object.prototype anymore:

    const n = 8;
    console.log(typeof n.hasOwnProperty);           // function
    console.log(Object(n) instanceof Number);       // true
    console.log(Object(n) instanceof Object);       // true
    Object.setPrototypeOf(Number.prototype, null);
    console.log(typeof n.hasOwnProperty);           // undefined
    console.log(Object(n) instanceof Number);       // true
    console.log(Object(n) instanceof Object);       // false

    (Object(n) returns a Number object for the number primitive n. I used it there because instanceof is always false for primitives [n instanceof Number is false, for instance]. To check inheritance, we need an object temporarily.)

    As you can see, the link between Number objects and Object has been broken.

    Again, this is a very bad idea in the general case, as it will tend to break things. Just because we can do it doesn't mean we should.

    However, I know I can add certain item in the middle of chain (like in Number-Object) by following code:

    Number.prototype.alpha = "def";
    let num5 = 99; 
    console.log(num5.__proto__);
    

    That's just adding a property to Number.prototype, not inserting something into the prototype chain. But it's possible to insert into the prototype chain, too, since we can change the prototype of Number.prototype:

    function Custom() {
    }
    // Change `Custom.prototype` to an object whose prototype
    // is the prototype of `Number.prototype` (which will be
    // `Object.prototype` in an unsullied environment).
    // (`Object.create` creates an object setting its prototype
    // to the given object.)
    Object.defineProperty(Custom, "prototype", {
        value: Object.create(Object.getPrototypeOf(Number.prototype)),
        writable: true,
    });
    Object.defineProperty(Custom.prototype, "constructor", {
        value: Custom,
        writable: true,
        configurable: true,
    });
    Object.defineProperty(Custom.prototype, "example", {
        value() {
            return "hi there";
        },
        writable: true,
        configurable: true,
    });
    Object.setPrototypeOf(Number.prototype, Custom.prototype);
    
    const n = 8;
    console.log(n.example());                 // "hi there"
    console.log(Object(n) instanceof Custom); // true

    I used a constructor function there just so we could readily use instanceof to check inheritance, but you can insert a prototype without a constructor function. Here's that same code without one:

    const custom = Object.create(Object.getPrototypeOf(Number.prototype));
    Object.defineProperty(custom, "example", {
        value() {
            return "hi there";
        },
        writable: true,
        configurable: true,
    });
    Object.setPrototypeOf(Number.prototype, custom);
    
    const n = 8;
    console.log(n.example()); // "hi there"


    Note 1: Changing the prototype of an existing objects (e.g., with Object.setPrototypeOf) is best avoided. JavaScript engines optimize on the assumption (which is normally true) that the prototype of an object won't change once it's been created. Changing it breaks that assumption, which removes those optimizations from the object.

    Note 2: You'll notice I didn't use the __proto__ accessor property anywhere above. It's deprecated and should not be used in new code, use Object.getPrototypeOf and (if absolutely necessary) Object.setPrototypeOf instead. It would also fail for any object that doesn't inherit from Object.prototype, since that's where the __proto__ accessor property is defined.