Harlan
Harlan

Reputation: 19401

When I use a method as a callback, it seems to lose access to `this`. Why?

I'm using express in Node to create a simple web app. The code looks like this:

var get_stuff = function (callback) {
    another.getter(args, function (err, data) {
        if (err) throw err;

        data = do_stuff_to(data);

        callback(data);
    });
};

app.get('/endpoint', function (req, res) {
    get_stuff(res.send);
});

When I run this, though, I get this error: TypeError: Cannot read property 'method' of undefined at res.send. The express code that's breaking starts like this:

res.send = function (body) {
    var req = this.req;
    var head = 'HEAD' == req.method;

It seems to me that the way I've constructed the callbacks is losing this in the send method. But I'm not sure how to fix it. Any tips? Thanks!

Upvotes: 1

Views: 344

Answers (2)

dumbass
dumbass

Reputation: 27238

In JavaScript, the value of this is generally determined by the call site, and unlike in Python, accessing a method via the . operator does not bind its left-hand side to this when the method is later called.

To perform the binding, you can call .bind like in the older answer, or you may perform the binding by hand, wrapping the method call in another callback:

get_stuff(function () {
    return res.send.apply(res, arguments);
});

As of ECMAScript 2018, it’s also possible to use fat-arrow function syntax and rest parameters to make the above much more compact:

get_stuff((...args) => res.send(...args));

Upvotes: 0

Felix Kling
Felix Kling

Reputation: 817030

Call .bind:

get_stuff(res.send.bind(res));

and have a look at the MDN documentation about this to get an idea how it works. The value of this is determined by how the function is called. Calling it "normally" (what probably happens with the callback), like

func();

will set this to the global object. Only if a function is called as an object method (or if this is explicitly set with .bind, .apply or .call are used), this refers to the object:

obj.fun(); // `this` refers to `obj` inside the function

.bind allows you to specify the this value without calling the function. It simply returns a new function, similar to

function bind(func, this_obj) {
    return function() {
        func.apply(this_obj, arguments);
    };
}

Upvotes: 3

Related Questions