0

I'm trying to determine the best way to allow a user to pass a class's method into a promise chain. Currently the code looks like this, and I get the error I expect.

class Parent {
        async say(word) {
                console.log(word);
        }
}

class MyClass extends Parent {
        async start() {
                console.log("OK, starting");
                return "starting";
        }

        async doThis(something) {
                this.say(something);
        }
}

async function boot() {
        const cl = new MyClass();
        await cl.start().then(cl.doThis); <-- Scoping issue
}

boot();

// Output
OK, starting
code.js:16
        this.say(something);
             ^

TypeError: Cannot read properties of undefined (reading 'say')
    at doThis (code.js:16:8)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async boot (code.js:22:2)

I fully expect that error, because (from what I understand) when I pass the cl.doThis as part of the resolution chain, I'm actually passing in the function itself and this is getting re-bound. Please correct me if I'm wrong on that assumption, but again I expect that to fail.

My response would be to change to a proper function so the proper binding is kept:

async function boot() {
        const cl = new MyClass();
        await cl.start().then(response => cl.doThis(response));
}

What I'm being told though it to use super instead of this when I'm calling parent functions.

class MyClass extends Parent {
        // ...
        async doThis(something) {
                super.say(something);
        }
}

This just avoids the error as we are no longer using this, it seems very heavy-handed to always call super.<method>() and end up having to keep track of which methods are being invoked from the parent class (and this is ignoring if one child class overrides a parent one, now I've got some classes invoking super and others using this).

Is there a better way to solve this, or is it OK to just say you need to wrap the class method calls in a proper function to keep scope?

dragonmantank
  • 15,243
  • 20
  • 84
  • 92
  • 1
    The preferred way is to pass `cl.doThis.bind(cl)`. – Sebastian Simon Oct 13 '22 at 23:49
  • @SebastianSimon I'm good with that as a solution as well, but I'm getting pushback because its "not obvious" to the developer, and the developer expects to be able to pass in the method without any changes. – dragonmantank Oct 13 '22 at 23:51

1 Answers1

0

I'm being told though it to use super instead of this when I'm calling parent functions:

class MyClass extends Parent {
        async doThis(something) {
                super.say(something);
        }
}
async function boot() {
        const cl = new MyClass();
        Promise.resolve().then(cl.doThis);
}

While this works and avoids the error of this being undefined, …

No, it doesn't work. The this binding is still undefined. The only difference is that you're no longer using it, neither in doThis nor in say. But if you did console.log(this, word) in the parent method, you'd realise it's actually undefined. This is because super.say(something) is just syntactic sugar for

Object.getPrototypeOf(MyClass.prototype).say.call(this, something);

The correct way to pass methods as callbacks is still to use .bind() or an arrow function.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Sorry, I'll clean that sentence up. You are correct that `this` is undefined, it wasn't my intention to make it sound like `this` was bound properly - you are correct, it's still undefined, we just sidestepped the error. – dragonmantank Oct 14 '22 at 01:09
  • @dragonmantank If you already knew that, then what exactly is your question? And no, your updated phrasing "*While this technically works*" is still misleading - it *doesn't* work. – Bergi Oct 14 '22 at 01:17
  • I do not use JavaScript day-in-day-out like I do other languages, especially ES6 and newer variants, so my question is about am I missing something when I'm being told by another, potentially more "experienced" JS developer I'm writing my code wrong and I shouldn't use `this` inside of classes, and I am misunderstanding how scoping works. From your linked post I was initially correct and my code was correct, the other dev's invocation and expectation was wrong. – dragonmantank Oct 14 '22 at 01:47
  • 1
    Not using `this` inside `class` methods is wrong, or at least weird. If you don't access the instance (`this`), then you shouldn't be writing an instance method but just a plain function or at best a static class method. – Bergi Oct 15 '22 at 03:28