Search code examples
javascriptnode.jsecmascript-6es6-promise

This undefined in fat arrow function but is defined in the enclosing scope


I am trying to do something fairly simple:

I have a function that is part of a class, in the function I call an async method and I use the .then syntax to listen for its completion. I then use an arrow function to perform an action.

This is where something goes wrong, inside my arrow function, this is undefined according to the VS Code debugger. But, this is defined in the function (write(document)) containing the arrow function.

Here is the code for that:

const fs = require('mz/fs');
const path = require('path');

class DB {

    constructor(name) {
        this.db = path.normalize("./");
    }

    write(document) {
        const dir = path.normalize(this.db + "/" + document);
        fs.exists(dir).then((exists) => {
            console.log(this); // ADDED THIS IN EDIT.
            if (!exists) {
                return fs.mkdir(dir)
            } else {
                return new Promise();
            }
        }).then(() => {
            const file = path.normalize(dir + "/" + document + ".json");
            fs.readFile(file, (err) => {
                console.log(err);
            });
        });
    }
}

I thought to have read that the arrow function automatically binds this from the scope where the arrow function defined is. But perhaps I misunderstood? I work mainly with Dart in which the scope rules aren't as complicated so perhaps I am mixing something up with Dart?

--EDIT-- Sorry for the confusion, I actually didn't need this in my arrow function but was just surprised that the VS Code debugger showed it as undefined. I now added a console log in the arrow function and apparently it is defined! Thus perhaps there is something wrong with the VS Code Node debugger? So after all, there doesn't seem to be a problem with the code anyways... enter image description here


Solution

  • You have three different arrow functions and none of them attempts to use this.

    I actually do not need this in any of the arrow functions, but upon debugging I found that this is undefined in their scope for some reason.

    This is a performance optimisation.

    If a variable isn't used in a function then it will not exist in that function, even if it would otherwise be in scope.

    This allows, for example:

    function foo () {
        var bar = something_that_returns_a_memory_intensive_object()
        return () => console.log(1);
    }
    
    const baz = foo();
    

    After foo has finished running and returns its function, bar (and the large object) can be garbage collected.

    The same is true of this.

    I now added a console log in the arrow function and apparently it is defined!

    … and when you use it, the variable is closed over and made available.