Confusion about this in callbacks

Viewed 55

I am really confused about the value of this in callbacks, well I know that the value of this is always taken from where it was called, but here in the below code, i can't figure out what's happening.

userSchema.pre("save", function(next) {
    let user = this;
    bcrypt.hash(user.password, 10, function(err, hash) {
        if (err) {
            next(err)
        } else {
            user.password = hash;
            next();
        }
    })
});

I was watching a tutorial of node js with mongoose, so the instructor said: In the context of this callback function, the word this refers to the object, which is

let userSchema = new mongoose.Schema({
    password: {
        type: String,
        required: true
    }
});

shouldn't the user = this here refer to Node Global Object instead of that object?

so I'm really confused how this happens, for Example, if I try to mimic this code behaviour in simple JavaScript.

function UserSchema() {
    this.pre = function(cb) {
        return cb();
    }
}

function Bycrypt() {
    this.hash = function(cb) {
        return cb();
    }
}

userSchema.pre(function() {
    var user = this;
    bycrypt.hash(function() {
        console.log(user)
    })
});

this will log user as the Window Object because the callback function was called in the context of Window.

well, I know that it's a weird question to ask.

3 Answers

this in JavaScript functions refers to execution context and in a case of normal (not ES6-specific "arrow" function) normally equals to context, function was called in. However "this" can be changed by e.g. using bind, call or apply methods of the Function object to pass different value for this.

You can read, for example, this article on MDN for details.

This is not a weird question at all, the this keyword is often a source of confusion because of its implicit nature :-) Anyway, I have no idea about how the pre function is implemented, but we can easily imagine something like that :

schema = {
  whoami: "a schema"
};
schema.pre = function (cb) {
  return cb.call(this);
}
schema.pre(function () {
  console.log(this.whoami);
});

The call function is a JavaScript builtin that allows to change the default subject (window) of a function. Browsing the source code I have found something that seems to be the pre function (not 100% sure) : https://github.com/Automattic/mongoose/blob/master/lib/schema.js#L1138. As you can see they use the apply builtin which has the same effect as call. Let's see how they differ :

function f(a, b) {
  console.log("I am", this.whoami);
  console.log("Arguments :", "[" + a + ", " + b + "]");
}

f.call({ whoami: "A" }, "b", "c");
f.apply({ whoami: "X" }, ["y", "z"]);

I believe that this can be a good starting point to investigate. I let you dive into the source code to find the nature of this.s.hooks and this.queue(...) :-)

this binds to the call site of a function. It can also be bind to where you specify it to bind to using function methods like call, apply, and bind.

function foo () {
  var a = 5;
  console.log(this.a);
}

var a = 2;

foo(); // 2

With ES6 arrow function, this is bound to the lexical scope.

Related