adapted from Chapter 2 in "You Don't Know JS" here
Key Point: The call-site determines where this will reference during the execution of a function. There are essentially 4 rules governing how the call-site determines where this will point during the execution of a function.
Rule 1. Default Binding when not in 'strict' mode
function foo() {
console.log(this.a);
}
var a = 2;
foo();
Rule 2. Implicit Binding
call-site uses obj context to reference the function. When there is a context object for a function reference, the implicit binding rule says that it's that object which should be used for the function call's this binding.
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2
Rule 3. Implicity Lost Binding & Explicit Binding
Implicity Lost Binding:
Even though bar appears to be a reference to obj.foo, in fact, it's really just another reference to foo itself. Moreover, the call-site is what matters, and the call-site is bar(), which is a plain, un-decorated call and thus the default binding applies.
function foo() {
console.log( this.a );
}
function doFoo(fn) {
// `fn` is just another reference to `foo`
fn(); // <-- call-site!
}
var obj = {
a: 2,
foo: foo
};
var a = "oops, global"; // `a` also property on global object
doFoo( obj.foo ); // "oops, global"
Explicit Binding: We create a function bar() which, internally, manually calls foo.call(obj), thereby forcibly invoking foo with obj binding for this. No matter how you later invoke the function bar, it will always manually invoke foo with obj. This binding is both explicit and strong.
function foo(something) {
console.log( this.a, something );
return this.a + something;
}
// simple `bind` helper
function bind(fn, obj) {
return function() {
return fn.apply( obj, arguments );
};
}
var obj = {
a: 2
};
var bar = bind( foo, obj );
var b = bar( 3 ); // 2 3
console.log( b ); // 5
Rule 4. new Binding
By calling foo(..) with new in front of it, we've constructed a new object and set that new object as the this for the call of foo(..)
function foo(a) {
this.a = a;
}
var bar = new foo( 2 );
console.log( bar.a ); // 2
Order of precedence of these 4 rules:
4 > 3 > 2 > 1
i.e. new > explicit > implicit > default