10

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.

0

3 Answers 3

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);
Sign up to request clarification or add additional context in comments.

6 Comments

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???).
@mikerodent The above method does not work for ES6 classes. It's a bit more complicated there.
Note that this only works for proper functions, not for arrow functions
@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')
|
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

1 Comment

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.
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

7 Comments

Is it possible to redefine the constructor in a similar way? I checked Reflect.construct() and it does not have the third parameter for redefining like Reflect.defineProperty() does.
@IanY. I recommend you create a child class, also you can check - stackoverflow.com/questions/55760091/…
Is creating a child class the only solution? I prefer redefining the constructor of the original class.
@IanY. it's the best practice. what is your purpose to redefine a constructor without inheritance?
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.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.