There is a subtle difference between an object with methods and an object with callbacks.
The this
of a method is the receiver of the method call (e.g. obj
if the method call is obj.m(···)
).
For example, you can use the WHATWG streams API as follows:
let surroundingObject = {
surroundingMethod() {
let obj = {
data: 'abc',
start(controller) {
···
console.log(this.data); // abc (*)
this.pull(); // (**)
···
},
pull() {
···
},
cancel() {
···
},
};
let stream = new ReadableStream(obj);
},
};
That is, obj
is an object whose properties start
, pull
and cancel
are methods. Accordingly, these methods can use this
to access object-local state (line *) and to call each other (line **).
The this
of an arrow function is the this
of the surrounding scope (lexical this
). Arrow functions make great callbacks, because that is the behavior you normally want for callbacks (real, non-method functions). A callback shouldn’t have its own this
that shadows the this
of the surrounding scope.
If the properties start
, pull
and cancel
are arrow functions then they pick up the this
of surroundingMethod()
(their surrounding scope):
let surroundingObject = {
surroundingData: 'xyz',
surroundingMethod() {
let obj = {
start: controller => {
···
console.log(this.surroundingData); // xyz (*)
···
},
pull: () => {
···
},
cancel: () => {
···
},
};
let stream = new ReadableStream(obj);
},
};
let stream = new ReadableStream();
If the output in line * surprises you then consider the following code:
let obj = {
foo: 123,
bar() {
let f = () => console.log(this.foo); // 123
let o = {
p: () => console.log(this.foo), // 123
};
},
}
Inside method bar()
, f
and o.p
work the same, because both arrow functions have the same surrounding lexical scope, bar()
. The latter arrow function being surrounded by an object literal does not change that.
Chapter “Callable entities in ECMAScript 6” in ”Exploring ES6”.