Arrows and
I am browsing twitter and found this tweet:
https://twitter.com/_ericelliott/status/855598297939144704
Here's the code from the tweet:
const double = () => this.x * 2;
const numDouble = double.bind({ x: 5 });
numDouble();
When you run this snippet in the console, it will create NaN. How? The author explicitly binds the value of x, but still shows NaN.
The author also points out that the arrow function cannot link this. As I know the arrow function lexically binds the meaning of this shape surrounding the area. Then why is the author claiming this?
Please clarify my doubts and in advance for your help.
source to share
Arrow functions do not bindthis
. According to MDN:
Lack of binding this
As long as the arrow functions, each new function defines its own value
this
(new object in the case of a constructor, undefined in strict mode function calls, a context object if the function is called as an "object method", etc.). This turned out to be annoying object-oriented programming style.
So this
your example will be a global object window
that doesn't appear to have a property called x
.
Example:
function foo() {
let arrow = () => {
console.log(this); // will use foo 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)
source to share
The main thing to remember:
- Arrow functions are closed over
this
, just like functions are closed over variables. (Actually, this is the same mechanism.) Regardless ofthis
where the arrow function is created, whatthis
happens when that arrow function is called. This will never happen again. Arrow functions ignorethis
they are called.
If you remember this, you will never be confused this
about the arrow function again.
When you run this snippet in the console, it will output NaN. How? The author explicitly binds the value of x, but it still shows NaN.
numDouble = double.bind({ x: 5 })
creates a new function ( numDouble
) that, when called, will call the original function ( double
) with the this
specified as the first argument bind
( { x: 5 }
). But since the arrow functions are to ignore this
they are called with, bind
it cannot control what this
they use.
The author also points out that the arrow function cannot relate this. As I know the arrow function lexically binds the value of this form surrounding the scope.
Yes, that means you cannot change it. Lexical binding is a way to close. This arrow function:
const a = () => {
console.log(typeof this);
};
handles this
exactly the same way this traditional function handles thisWhereFunctionWasCreated
:
const thisWhereFunctionWasCreated = this;
const t = function() {
console.log(typeof thisWhereFunctionWasCreated);
};
Just as you cannot change what a thisWhereFunctionWasCreated
variable is t
using when you call it, you cannot change what is this
used when you call it. (If thisWhereFunctionWasCreated
it is not const
, you can change the value it contains, but not that of the thisWhereFunctionWasCreated
variable thisWhereFunctionWasCreated
t
. But in this example, it is a constant because it is a this
constant.)
Since the arrow function completely ignores the this
caller, it doesn't matter what mechanism is used to try and tell the arrow function what to this
use, it won't work. obj.arrow()
whether you are this
implicitly calling the function as a method ( obj.arrow()
), either with call
or apply
( arrow.call(obj)
), or with bind
( const boundArrow = arrow.bind(obj); boundArrow();
), it will still use instead this
:
"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 that the bind
arrow function can do 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 arrowWith2.name
"bound x"
[where x
is the name of the original function. So arrowWith2.name
in the above arrowWith2.name
, this is "bound arrow"
.)
source to share