Search code examples
javascriptclassprototypees6-class

How to define prototype method inside class definition (externally) in Javascript?


I have a class declaration which is quite large with lots of methods.

It's something like this:

class Foo {
  constructor() {
    // ...code
  }
  config() {
    // ...code
  }
  theme() {
    // ...code
  }
  register() {
    // ...code 
  }
  render() {
    // ...code
  }
  write() {
    // ...code
  }
}

I'd like to split the source code up so it's easy to manage and read.

Is there a way I can assign a method to a class dynamically within the class itself?

I'd like to do something like:

function bar(value) {
  if (value) {
    this.baz = 'baz'
    return this
  }
}

class Foo {
  constructor() {
    // ...code
  }
  new bar() // Would reference function above ^ (not valid)
}

I know this is not valid or correct but because the class syntax automatically creates and binds a property called bar() to the prototype of Foo I can't think how you would do it, and perhaps the functionality doesn't exist.

The closest I can think to splitting the code up, is to extend a class but this seems a bit verbose.

class Bar {
  bar() {
    if (value) {
      this.baz = 'baz'
      return this
    }
  }
}
class Foo extends Bar {
  constructor() {
    super()
  }
}

bar() is now in Foo's prototype.

This method also offers little control to creating aliases if needed and also is harder to make it clear what methods that are available in the Foo class.

Is there a better way?


Solution

  • I can think of two ways of adding external functions as own properties:

    1. Set it as a property of the this inside the constructor of the class.
    2. Set it as a public property of the class, this is the proposal.

    The downside is that, all the instances will not share the same function object as it becomes an own property. Own properties are used for holding state not functions.

    To add in prototype, which is where methods in ES6 class are added, so that all instances share the same function object. You have to add it to the prototype property of the class. This works as class is a syntactic sugar of the function constructor and every function has a prototype property which is inherited by all instances of that class.

    class Foo {
      constructor() {
        // ...code
        this.bar = bar; //becomes own property
      }
      
      //As a public field, becomes own property
      baz = baz;
      
    }
    function bar(){
      console.log("from bar");
    }
    function baz(){
      console.log("from baz");
    }
    function foobar(){
     console.log("from foobar");
    }
    
    const foo = new Foo();
    foo.bar();
    foo.baz();
    //Set it directly on the prototype prop of the Class
    Foo.prototype.foobar = foobar; 
    foo.foobar();

    Also, I presume there isn't a way to set a prototype of a class inside the class definition itself?

    From your question in the comment, yes you can set the function in the prototype of the class inside the class definition itself:

    function bar(){
      console.log("bar in prototype");
      console.log("accessing data from this: ", this.data);
    }
    
    class Foo{
     constructor(){
       this.data = "data"
       Foo.prototype.bar = bar;
       Foo.prototype.thisBar = bar.bind(this);
     }
     //methods are added in prototypes
     baz(){
      console.log("baz in prototype");
     }
    }
    
    const foo = new Foo();
    foo.bar();
    foo.baz();
    //accessing from prototype, here this is global context and will print undefined
    Object.getPrototypeOf(foo).bar();
    //this is bound so always refers to the instance
    Object.getPrototypeOf(foo).thisBar();
    //same for methods in es6
    Object.getPrototypeOf(foo).baz();

    For static methods just set it either as a property of the class or set it as public static property.

    class Bar{
      constructor(){
        //some logic
      }
      static baz = baz;
    }
    function foo(){
      console.log("from foo");
    }
    function baz(){
      console.log("from baz");
    }
    Bar.foo = foo;
    Bar.foo();
    Bar.baz();