7

In ES5 we all could do like this:

myClass.prototype.myMethod = (function () {return function() {}})();

Am I able to do the same trick with ES6 class literals?

Tristan Tzara
  • 5,157
  • 5
  • 22
  • 38

5 Answers5

12

No, not yet at least. ES6 classes only have support for declaring methods, so anything that's not directly a method (this includes things that indirectly evaluate to a method, such as IIFE) must still be declared with the prototype.

However, ES6 classes really work the same as ES5 constructor functions do, only with a bit cleaner syntax, so you can still do this:

class MyClass {
  constructor() {
    /* initialize */
  }

  regularMethod() {
    /* some stuff */
  }
}

MyClass.prototype.myMethod = (function() { return function() })()

which would be equivalent to this:

function MyClass() {
  /* initialize */
}

MyClass.prototype.regularMethod = function() {
  /* some stuff */
}

MyClass.prototype.myMethod = (function() { return function() })()
Frxstrem
  • 34,562
  • 9
  • 73
  • 106
6

2019 UPDATE


YES, you can do it.

You just need to create the IIFE like a "function expression" (assign it to a variable)

class MyClass {

  IIFE = (() => {

    let textArrayCreatedJustOnce = ['text A', 'text B', 'text C'];
    console.log('Only called in object creation');


    return () => {
      console.log(textArrayCreatedJustOnce[1]);
    }

  })()

}


let myClassInstance = new MyClass(); //log: 'Only called in object creation' 


myClassInstance.IIFE(); //log: 'text B'
myClassInstance.IIFE(); //log: 'text B'
myClassInstance.IIFE(); //log: 'text B'
Juanma Menendez
  • 12,842
  • 6
  • 48
  • 46
  • 1
    Your example seems to work, but 1. Uses arrow functions and 2. never uses the this keyword. My first try with a common function expression and usage of this failed. Unlike normal function expressions, arrow functions inherit the local calling context. It is somewhat unclear what that context is in this case, though. I tried to replace the () call with .call(this), but it didn't help (what is this?). I will try to sort out what the problem is, but am of course not certain to solve it. Anyway, this may possibly be a bit too high-tech for deployment, as many users will have pre-2019 browsers. – Elias Hasle May 10 '19 at 11:53
  • Sorry, my bad. See my new answer (in a few minutes). – Elias Hasle May 10 '19 at 11:59
  • 2
    It may not use `this`, but it can. It's not too high tech. Those assignments are done in the constructor, don't be scared. It's as if you wrote `constructor() {this.IIFE = ...}` See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#Field_declarations . – Juan Mendes May 10 '19 at 14:37
  • Thanks for constructor() {this.IIFE = ...} it gives ability to pass initial value – Igor Fomenko Jun 13 '20 at 18:22
  • @JuanMendes I've looked at this and similar solutions to other problems (such as [here](https://stackoverflow.com/questions/33554120/incrementing-object-id-automatically-js-constructor-static-method-and-variable)) and I'm wondering if the arrow function being returned would act as an implicit constructor function? Would you define properties in that function? – DeltaFlyer Jan 06 '22 at 06:08
3

It's possible to crate a decorator:

function iife(target, key, { value: fn, configurable, enumerable }) {
  return {
    configurable,
    enumerable,
    value: fn(),
  };
}

And use it like:

class MyClass {
  @iife
  methodName() {
    /* some stuff */
    return function() {
      /* real method content */
    }
  }
}

I use it if I need some heavy temporary variables like matrices of vectors that I don't want to crate for each method call.

3

The 2019 answer from @juanma-menendez is interesting, although I was left with some doubts and unanswered questions.

The first problem is that it uses arrow functions. Arrow functions handle calling context differently than ordinary function expressions. Also, they don't support recursion (as far as I know).

The second problem is that it never tries the this keyword.

It is somewhat unclear what the calling context is in this syntactic syrup.

After some unsuccessful tries, I succeeded with the following setup in Google Chrome. This example demonstrates that both private and object attributes can be accessed from the returned method, and that it works with multiple instances:

class C {
    constructor(attr) {
        this.attr = attr;
    }
    IIFE = function() {
        const privateArray = ["A", "B", "C"];
        console.log("Created private array during object creation.");

        return function() {
            console.log("privateArray[1] = %s", privateArray[1]);
            console.log("this.attr = ", this.attr);
        }//.bind(this);
    }.call(this);
}

var obj1 = new C("asdf");
console.log(obj1.IIFE());
console.log(obj1.IIFE());
var obj2 = new C("ghjk");
console.log(obj2.IIFE());

Outputs (undefined is the return value):

Created private array during object creation.
privateArray[1] = B
this.attr =  asdf
undefined
privateArray[1] = B
this.attr =  asdf
undefined
Created private array during object creation.
privateArray[1] = B
this.attr =  ghjk
undefined

I must admit I don't understand why .bind(this) was wrong. I am also a bit confused that the private array is created for each instance, and does not live solely in the prototype. I am not 100% sure this is equivalent to a prototype method made with IIFE.

Anyway, this may all possibly be a bit too high-tech for deployment, as many end users will have pre-2019 browsers. Also, I have not tested the performance. Use at own risk! :-)

Elias Hasle
  • 557
  • 5
  • 13
0

No, class literals cannot contain an IIFE, they need to consist of method declarations.

You can use the same assignment in ES6 though after having declared myClass using the class keyword. However there's not really any good reason to use IIFEs in ES6 at all, given they can usually be replaced by block scopes or modules.

Bergi
  • 572,313
  • 128
  • 898
  • 1,281