arrow function and this
Arrow functions don't bind this
. As per MDN:
No binding of this
Until arrow functions, every new function defined its own
this
value (a new object in the case of a constructor, undefined in strict mode function calls, the context object if the function is called as an "object method", etc.). This proved to be annoying with an object-oriented style of programming.
So this
in your example will be the global object window
which apparently don't have a property called x
.
Example:
function foo() {
let arrow = () => {
console.log(this); // will use foo's this as arrow will never have its own this
}
arrow.call({"x": "x"}); // ... even if we specify it using bind, call, or apply
}
foo.call({"y": "y"}); // specifying the this for foo (this value will eventually be used by arrow because it will be availbale in its scope)
The key thing to remember is:
-
Arrow functions close over
this
, exactly the way that functions close over variables. (In fact, it's the same mechanism.) Whateverthis
is where the arrow function is created is whatthis
will be during a call to that arrow function. It will never be anything else. Arrow functions ignore thethis
they're called with.
If you remember that, you'll never be confused by this
in an arrow function again.
When you run this snippet in console it'll produce NaN. How? Author is explicitly binding the value of x, but still it's showing NaN.
numDouble = double.bind({ x: 5 })
creates a new function (numDouble
) that, when called, will call the original function (double
) with this
set to the value you provide as bind
's first argument ({ x: 5 }
). But since arrow functions ignore the this
they're called with, bind
can't control what this
they use.
Author is also specifying that arrow function can't bind this. As i know that arrow function lexically bind the value of this form surrounding scope.
Right, which means you can't change it. Lexical binding is the way closures work. This arrow function:
const a = () => {
console.log(typeof this);
};
treats this
exactly the way this traditional function treats thisWhereFunctionWasCreated
:
const thisWhereFunctionWasCreated = this;
const t = function() {
console.log(typeof thisWhereFunctionWasCreated);
};
Just like you can't change what thisWhereFunctionWasCreated
variable t
uses when you call it, you can't change what this
a
uses when you call it. (If thisWhereFunctionWasCreated
weren't a const
, you could change the value it holds, but not which thisWhereFunctionWasCreated
variable t
uses. But it's a constant in that example because this
is a constant.)
Since an arrow function completely ignores the this
it was called with, it doesn't matter what mechanism you use to try to tell the arrow function what this
to use, it won't work. Whether you specify this
implicitly by calling the function as a method (obj.arrow()
), or via call
or apply
(arrow.call(obj)
), or via bind
(const boundArrow = arrow.bind(obj); boundArrow();
), it will still use the this
it closes over instead:
"use strict";
function Ctor() {
// `this` will be the object created by `new Ctor`; grab it
this.name = "outerThis";
const outerThis = this;
// `traditional` doesn't close over `this`, so you CAN change
// what `this` it uses when you call it, in various ways
function traditional(testNum) {
console.log(testNum, "traditional:", getName(this));
}
// `arrow` closes over `this`, so you CAN'T change
// what `this` it uses when you call it
const arrow = testNum => {
console.log(testNum, "arrow: ", getName(this));
};
// Remember that the `this` in a direct call is the global
// object in loose mode, `undefined` in strict mode; this
// code is in strict mode
console.log("Direct call (default `this`):");
traditional(1); // 1 traditional: window
arrow(1); // 1 arrow: outerThis
console.log("`obj.xyz()`:");
const obj = {
name: "obj",
arrow,
traditional
};
obj.traditional(2); // 2 traditional: obj
obj.arrow(2); // 2 arrow: outerThis
console.log("Using `call`:");
traditional.call(obj, 3); // 3 traditional: obj
arrow.call(obj, 3); // 3 arrow: outerThis
console.log("Using `bind` and calling result:");
const boundTraditional = traditional.bind(obj);
const boundArrow = arrow.bind(obj);
boundTraditional(4); // 4 traditional: obj
boundArrow(4); // 4 arrow: outerThis
}
function getName(t) {
switch (t) {
case undefined:
return "undefined";
case window:
return "window";
default:
return t.name;
}
}
new Ctor();
.as-console-wrapper {
max-height: 100% !important;
}
The only thing bind
can do when called on an arrow function is bind arguments to it:
const arrow = (x, y) => x + y;
console.log(arrow(2, 3)); // 5
const arrowWith2 = arrow.bind(null, 2);
console.log(arrowWith2(3)); // 5
const arrowWith2And3 = arrow.bind(null, 2, 3);
console.log(arrowWith2And3()); // 5
(It also sets the name of the resulting function to "bound x"
[where x
is the name of the original function. So arrowWith2.name
in the above is "bound arrow"
.)