4

I am trying to overwrite the function of a class:

class MyClass {
  constructor() {
    // more code
  }

  myFunction = function() {
    console.log('not this')
  }
}

// can't change the code above

MyClass.prototype.myFunction = function() {
  console.log('it should print this')
}

new MyClass().myFunction()

But Babel compiles the above to this:

class MyClass {
  constructor() {
    // more code

    this.myFunction = function () {
      console.log('not this');
    };
  }
}

// can't change the code above

MyClass.prototype.myFunction = function () {
  console.log('it should print this');
};

new MyClass().myFunction();

Because the function is defined as a property in the original code, Babel puts that definition in the constructor. If I understand correctly prototype only contains functions, not all properties. Because the constructor runs after the object was derived from the prototype, I can't use the prototype to overwrite that function.

My second try was overwriting the constructor:

class MyClass {
  constructor() {
    // more code
  }

  myFunction = function () {
    console.log('not this')
  }
}

// can't change the code above

let oldConstructor = MyClass.prototype.constructor
MyClass.prototype.constructor = function() {
  // call old constructor first, it will set myFunction
  oldConstructor()

  // now overwrite myFunction
  this.myFunction = function () {
    console.log('it should print this')
  }
}

new MyClass().myFunction()

Well, let's try... Compile with Babel, save it to test.js and run:

~> node test.js
not this

I tried to make the question as general as possible. More background information on why I can't change the class in my specific case: The class is actually from a library I'm using and other packages I use depend on that library too. MeteorJS requires packages to specify the exact version and source of their dependencies, which is why I can't use a fork: I would have to fork every package that depends on this library.

4 Answers4

0

In fact you are changing your class, but it is "not taking" effect because how javascript interpreter looks for information inside its objects. First, properties inside object, then prototype chain.

In your first example, if you "remove" local propertie, you change takes effect. Example:

class MyClass {
  constructor() {
    // more code

    this.myFunction = function () {
      console.log('not this');
    };
  }
}

// can't change the code above

MyClass.prototype.myFunction = function () {
  console.log('it should print this');
};

const obj = new MyClass();
delete obj.myFunction;
obj.myFunction();

https://jsbin.com/gixufadewu/edit?js,console

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
Cleiton
  • 17,663
  • 13
  • 46
  • 59
  • This answer was not very helpful in finding a solution, but it is the most helpful answer for understanding the problem and structure. Since I can't mark my own answer as solution, I'll mark this one as solution because it helped me to understand the problem. – PetaByteBoy May 01 '17 at 20:41
0

This cannot be done. Whenever you instantiate MyClass, the inner myFunction is redefined. But not the myFunction defined in prototype chain. So the Interpreter will first look for the method in the instance and then in prototype chain. Define your methods in prototype chain which can be override with JavaScript inheritance.

eg:

var a = new MyClass();
var b = new MyClass();

a.myFunction() === b.myFunction(); //false

a.__proto__.myFunction() === b.__proto__.myFunction() //true
ajai Jothi
  • 2,284
  • 1
  • 8
  • 16
  • As I said I can't change the original class, but I can override the constructor with inheritance, which is not quite what I was looking for but it works just as well. – PetaByteBoy May 01 '17 at 20:40
0

Since there is no solution to the original question, I ended up using this:

class MyClass {
  myFunction = function() {
    console.log('not this')
  }
}

class MyNewClass extends MyClass {
  myFunction = function() {
    console.log('should print this')
  }
}

new MyNewClass().myFunction()

Obviously I always have to use MyNewClass now, which is something I don't really want and the original question asked for a solution to overwrite the function of the existing Class, but this works in my case.

0

What you can do is to intercept the assignment by putting a getter/setter on the prototype:

function myFunction() {
  console.log('it should print this');
}
Object.defineProperty(MyClass.prototype, "myFunction", {
  set(val) { /* ignore */ },
  get() { return myFunction; }
});

Alternatively, you could decorate the constructor, though for an ES6 class that means extra precautions.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375