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?

2
  • 1
    The preferred way is to pass cl.doThis.bind(cl). Commented Oct 13, 2022 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. Commented Oct 13, 2022 at 23:51

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

4 Comments

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

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.