ES6 - declare a prototype method on a class with an import statement

28,769

Solution 1

You can still attach a method on a class' prototype; after-all, classes are just syntactic sugar over a "functional object", which is the old way of using a function to construct objects.

Since you want to use ES6, I'll use an ES6 import.

Minimal effort, using the prototype:

import getColor from 'path/to/module';

class Car {
    ...
}

Car.prototype.getColor = getColor;

As you can see, you still use the prototype property to attach a method, should you choose to.


Calling the module within a class' method:

Alternatively, if you don't want to use the prototype property, you can always have your method return the function from the module:

import getColor from 'path/to/module';

class Car {
    getColor () {
        return getColor.call(this);
    }
}

Using a Getter

You could also be a bit tricky and use a "getter" to achieve this in a different manner.

import getColor from 'path/to/module';

class Car {
    get getColor () { return getColor.bind(this) }
}

You could then use it simply by calling, myInstanceOfCar.getColor()

Or in a more semantic usage of a getter:

class Car {
    get color () { return getColor.call(this) }
}

// ...

const color = myInstanceOfCar.color;

Keep in mind that getters/setters cannot have the same name as properties that you set in the constructor. You will end up exceeding the maximum call-stack with infinite recursion when you try to use the setter to set that same property. Example: set foo (value) { this.foo = value }


ES2016 Class Properties

If you're using Babel to transpile (and are using experimental proposals), and want to use some ES2016, you can use the following syntax (but keep in mind that this applies the method to the object directly, and does not set it on the prototype):

import getColor from 'path/to/module';

class Car {
    getColor = getColor;
}

Optional binding w/ class properties

If you use the shorthand syntax for setting a property, you won't have to bind the method (setting is as a property changes what "this" refers to, essentially automatically binding it), but you certainly can, should you choose to (like if you'd like to bind something else):

getColor = getColor.bind(this);

Solution 2

Yes. The class syntax is just (very sophisticated) syntactic sugar for constructor functions. So Car will still be a function with a prototype property and you can do exactly the same:

import getColor from './getColor';
// ...
Car.prototype.getColor = getColor;

However, that makes the method enumerable, as opposed to the methods created from the class syntax. So you might want to use Object.defineProperty instead.

Share:
28,769
SheedySheedySheedy
Author by

SheedySheedySheedy

Updated on July 13, 2022

Comments

  • SheedySheedySheedy
    SheedySheedySheedy almost 2 years

    I am using ES6 classes. I want to be able to do this:

    function Car(color) {
      this.color = color;
    };
    
    Car.prototype.getColor = require('./getColor');
    

    Where get color is an exported function. i.e. I want to be able to import a function from an outside file and set is as a prototype method on the ES6 Class. This is the kind of syntax I am talking about:

    class Car {
      constructor(color) {
        this.color = color;
      }
    
      getColor() {} // I want to import this function from './getColor', as above
    }
    

    Is this doable?

  • SheedySheedySheedy
    SheedySheedySheedy over 8 years
    Thanks for your answer. I understand that I can do this. I probably didn't make clear from the question that I wish to maintain the new syntax for imports in ES6, and also the ES6 classes way of declaring a prototype method (as above). Is that possible?
  • Felix Kling
    Felix Kling over 8 years
    FWIW, I only used require here for simplicity. Nothing prevents you from using import to get the value. As for the class syntax, no, ES6 does not provide a way to do that (except workarounds that delegate to the function as shown in the other answer). There is an ES7 stage 0 proposal for class properties, where you can write class Foo { getColor = require('./getColor'); }, but again, there is nothing like that in ES6.
  • ndugger
    ndugger over 8 years
    @SheedySheedySheedy If you want an ES6 import, see my answer. Imports are hoisted, though, so you have to declare them at the very top.
  • Bergi
    Bergi over 8 years
    No, you don't need to bind the function. It's still the method invocation that sets this.
  • Vitaliy Terziev
    Vitaliy Terziev over 7 years
    I am wondering, why are then the static methods. Is it not their purpose to superset the prototype (not working on the instance) methods hm...
  • mshameer
    mshameer over 7 years
    @FelixKling this solution is not working in react please look into this stackoverflow.com/questions/40603632/…
  • parker_codes
    parker_codes almost 5 years
    For anyone who is running into binding issues with adding to the prototype, these must be regular function declarations. Meaning you cannot be using imported arrow functions.
  • ndugger
    ndugger almost 5 years
    @GoogleMac It's because of this that I personally believe that arrows should be reserved for callbacks, where the lexical binding actually makes sense. Standalone function declarations should be used everywhere else (but this is just my personal preference)
  • Andris
    Andris over 2 years
    And if I'd like to add methods to standard classes like String? I'd like to import a file and then - preferably without any more code - have String extended with my additional methods. Thanks!
  • Andris
    Andris over 2 years
    Silly me. Just import './stringextender.js' - and it works