Search code examples
javascriptobject

Bind class to every new instance of a class in JavaScript


I've been taken a bunch of JavaScript riddles lately and got really stuck on this one for like a day and a half (for context, all the others took 1 hour max). Since I want to come up with the final solution myself, I will post a somewhat similar piece of code just so I can grab the general idea and implement it as per the context of the riddle.

Assume this is the piece of code:

class Vehicle {
  constructor(brand, model, power) {
    this.brand = brand;
    this.model = model;
    this.power = power;
  }

  info() {
    return `This vehicle is a ${this.brand} - ${this.model}, with a power of ${this.power}`;
  }
}


//do something with my class here such that
//the next calls will return/display as follows

const v = new Vehicle('Mercedes', 'C class', 12345);
console.log(v.info()); // This vehicle is a Mercedes - C class, with a power of 12345

const {
  info
} = v;
console.log(info()); // This vehicle is a Mercedes - C class, with a power of 12345



//To keep in mind, the function should stay the same, so a call to (info === v.info) should return true;
//And only the part of the code where it says to enter the code can be changed, so the class should be untouched.

I'm aware this shouldn't be a case where maybe it will be used in everyday life, as it is a riddle and just meant to challenge our brain, but it challenged me to the point I give up haha.

I tried looping over the entries of the Vehicle.prototype, and setting each one that is a function and is not the constructor to the same function bound to the class, or to the class.prototype or anything similar and it doesn't work.

How can I go about solving it, or at least make it so I can edit a function call for every time a new instance of that class is created?

Edit: I tried using the bind method, even tried something like this

Vehicle.prototype['info'] = Vehicle.prototype['info'].bind(Vehicle.prototype);

Vehicle.prototype['info'] = Vehicle.prototype['info'].bind(Vehicle);

Vehicle.prototype['info'] = Vehicle.prototype['info'].bind(Vehicle.prototype['info']);

I tried many alternatives and they didn't work since I have to have it bound to each instance of the class created. So if a new instance was created, and the same was done there, each one would return data according to the instance it was extracted from.


Solution

  • To keep info === v.info as true. You need to use bind().

    class Vehicle {
      constructor(brand, model, power) {
        this.brand = brand;
        this.model = model;
        this.power = power;
      }
    
      info() {
        return `This vehicle is a ${this.brand} - ${this.model}, with a power of ${this.power}`;
      }
    }
    
    const v = new Vehicle('Mercedes', 'C class', 12345);
    console.log(v.info());
    
    const info = v.info;
    console.log(info.bind(v)());
    
    console.log(info === v.info);

    Update:

    Another elegant solution by gog. I added it here since this question is closed.

    class Vehicle {
        constructor(brand, model, power) {
            this.brand = brand;
            this.model = model;
            this.power = power;
        }
    
        info() {
            return `This vehicle is a ${this.brand} - ${this.model}, with a power of ${this.power}`;
        }
    }
    
    
    let fn = Vehicle.prototype.info
    Object.defineProperty(Vehicle.prototype, 'info', {
        get() {
            return this._bound ?? (this._bound = fn.bind(this))
        }
    });
    
    const v = new Vehicle('Mercedes', 'C class', 12345);
    console.log(v.info());
    
    const {
        info
    } = v;
    
    console.log(info());
    
    console.log(info === v.info)