9

When using a constructor function in JavaScript to create a class, is it possible to redefine the class's method later?

Example:

function Person(name)
{
    this.name = name;
    this.sayHello = function() {
        alert('Hello, ' + this.name);
    };
};

var p = new Person("Bob");
p.sayHello();   // Hello, Bob

Now I'd like to redefine sayHello like this:

// This doesn't work (creates a static method)
Person.sayHello() = function() {
   alert('Hola, ' + this.name);
};

so when I create another Person, the new sayHello method will be called:

var p2 = new Person("Sue");
p2.sayHello();   // Hola, Sue
p.sayHello();    // Hello, Bob

EDIT:

I realize I could send in an argument like "Hello" or "Hola" to sayHello to accomplish the different output. I also realize I could simply assign a new function to p2 like this:

p2.sayHello = function() { alert('Hola, ' + this.name); };

I'm just wondering if I can redefine the class's method so new instances of Person will use the new sayHello method.

tronman
  • 9,862
  • 10
  • 46
  • 61

3 Answers3

14

is it possible to redefine the class's method later?

Yes. However, you must not assign the new function to a property of the Person constructor, but to the instance itself:

var p2 = new Person("Sue");
p2.sayHello();   // Hello, Sue
p2.sayHello = function() {
   alert('Hola, ' + this.name);
};
p2.sayHello();   // Hola, Sue

If you want to do this for all new instances automatically (and have not used the prototype for the method, which you easily could exchange as in @dystroy's answer), you will need to decorate the constructor:

Person = (function (original) {
    function Person() {
        original.apply(this, arguments);   // apply constructor
        this.sayHello = function() {       // overwrite method
            alert('Hola, ' + this.name);
        };
    }
    Person.prototype = original.prototype; // reset prototype
    Person.prototype.constructor = Person; // fix constructor property
    return Person;
})(Person);
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I tried your idea for "decorating the constructor". It doesn't currently seem to work, and I get a "requires new" error. I am not a JS expert but this may be something to do with newer versions of JS (I think I am currently using ES6 (async/await)... or is it ES7???). – mike rodent Sep 02 '17 at 07:24
  • 1
    @mikerodent The above method does not work for ES6 `class`es. It's [a bit more complicated](https://stackoverflow.com/a/31789308/1048572) there. – Bergi Sep 02 '17 at 08:52
  • Note that this only works for proper functions, not for arrow functions – Pum Walters May 15 '18 at 09:40
  • @PumWalters Sure, [arrow functions are no constructors](https://stackoverflow.com/q/34361379/1048572). – Bergi May 15 '18 at 11:23
  • @Bergi: I'm not sure I understand. An ordinary function can be assigned to an object property and will act as a method (i.e. will get 'this'). An arrow function will act as a function but not as a method (won't get 'this') – Pum Walters May 15 '18 at 14:59
  • @PumWalters I thought you were talking about the `Person` (constructor) function not the `sayHello` (method) function? But yes, of course you can't use arrow functions here, requiring dynamic `this`. – Bergi May 15 '18 at 15:11
13

To have a different function for p2, you can just set the sayHello property of p2 :

p2.sayHello = function(){ 
    alert('another one');
}
p2.sayHello(); 

If you use prototype, then you can also change it for all instances of Person (and still you can overwrite it for a specific person) :

function Person(name)
{
    this.name = name;
};
Person.prototype.sayHello = function() {
    alert('Hello, ' + this.name);
};

var p = new Person("Bob");

// let's set a specific one for p2
p2.sayHello = function(){ 
    alert('another one');
}

// now let's redefine for all persons (apart p2 which will keep his specific one)
Person.prototype.sayHello = function(){ 
    alert('different!');
}

p.sayHello();  // different!
p2.sayHello(); // another one
Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • 1
    I know about prototype and that I could assigned a new function to p2, but what I wanted was to change Person directly so new instances of Person would use the "Hola" function. Sorry I didn't make that more clear. – tronman Jan 20 '14 at 22:05
3

To solve your issue You can use Reflect object

class Person {
    public name: string;
    
    constructor(name: string) {
        this.name = name;
    }

    public sayHello(): void {
        console.log(`Hello, ${this.name}`)
    }
}

const p = new Person("Bob");

p.sayHello();   // Hello, Bob

Reflect.defineProperty(Person.prototype, 'sayHello', { value: function() {
    console.log(`Goodbye, ${this.name}`)
}})

p.sayHello();   // Goodbye, Bob
zemil
  • 3,235
  • 2
  • 24
  • 33
  • Is it possible to redefine the constructor in a similar way? I checked [Reflect.construct()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/construct) and it does not have the third parameter for redefining like [Reflect.defineProperty()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/defineProperty) does. – Ian Y. Mar 14 '23 at 10:37
  • @IanY. I recommend you create a child class, also you can check - https://stackoverflow.com/questions/55760091/how-to-override-a-base-class-constructor-in-javascript – zemil Mar 15 '23 at 07:59
  • Is creating a child class the only solution? I prefer redefining the constructor of the original class. – Ian Y. Mar 15 '23 at 08:33
  • @IanY. it's the best practice. what is your purpose to redefine a constructor without inheritance? – zemil Mar 15 '23 at 14:23
  • Could you please explain why it's the best practice? I prefer to use the existing class so that there wouldn't be an unnecessary new class added into my code. – Ian Y. Mar 15 '23 at 15:50
  • @IanY. if you use an existing class, you shouldn't change its constructor outside of that class - search the SOLID - one of principles will be an explanation of the best practice – zemil Mar 16 '23 at 09:05
  • Thanks. I searched it. I guess it's the second principle "open–closed principle"? But my case is that the class, which was not written by me, has some flaw in its constructor that needs to be fixed. And my purpose is fixing it and also adding some useful code to make things easier. Does that still not conform to the "open–closed principle"? – Ian Y. Mar 16 '23 at 11:45