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.) Whatever this is where the arrow function is created is what this will be during a call to that arrow function. It will never be anything else. Arrow functions ignore the this 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".)