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