It might seem politically incorrect not to use a few ES6 features now that ES6 is so popular. Node.green has a look at ES6 support for all the features I want to use, so I decided to use ES6 for the new project.

The first thing to do, of course, is to ruthlessly eliminate VAR. Let is not used in projects where const is used, and var is not used in projects where let is used.

The second thing is to replace function with the arrow function that working people like. When I was content to see a full screen of =>, reality hit me with a loud slap in the face – the corrected program is full of bugs!

So, when we use arrow functions, it is important to understand what arrow functions are and what situations they are applicable to. This article discusses the down arrow function for the above problems.

What is the arrow function?

I’m not going to talk about the syntax of the arrow function, I’m sure you’ve all seen it. Like me, 90% of what people like about the arrow function is that it’s pretty. Besides being pretty, is it equivalent to function? Surely not, because TC39 cannot introduce a syntactic sugar (except class) just because it looks good.

The origin of the arrow function goes back to ancient times with something called the lambda calculus. Lambda calculus is put forward by mathematicians, some mathematicians like our programmers are also very lazy, so many mathematical theorems, today to prove the triangle law, tomorrow to prove the hook law, tired not tired! Can all the proof problems be formalized in a unified system, and then be automatically derived by the machine? This is what the Lambda calculus was all about, and Alan had developed a system called a Turing machine, which was equivalent.

All this talk about lambda calculus doesn’t seem to have anything to do with today’s arrow function, right? The lambda calculus has a profound influence on the design of arrow functions. Mathematicians prefer purely functional programming languages, which have no side effects, always produce a deterministic output given a particular input, and in some cases can even be inferred from the output. To implement a pure function, the function must be executed independent of any external state. The whole function is like a mathematical formula. Given a set of input parameters, the result is the same whether it is executed on Earth or on Mars.

For arrow functions to behave like pure functions, external states must be removed. So when you define an arrow function, you don’t have any of the usual “this”, “arguments”, and “caller” functions.

Arrow function doesn’t have anythis

The arrow function does not have this, so the following code can obviously fetch this:

function foo() {
  this.a = 1
  let b = () => console.log(this.a)

  b()
}

foo()  // 1Copy the code

The this in the arrow function above is actually this in the parent scope of the function foo. The arrow function refers to the parent variable and forms a closure. The above code is equivalent to:

function foo() {
  this.a = 1

  let self = this
  let b = () => console.log(self.a)

  b()
}

foo()  // 1
Copy the code

Not only do arrow functions not have this, but also the usual arguments. If you can get arguments, it must be from the parent scope.

function foo() {
  return () => console.log(arguments[0])
}

foo(1, 2)(3, 4)  // 1Copy the code

In the example above, if the arrow function has arguments, it should output 3 instead of 1.

A common mistake is to use arrow functions to define object methods, as in:

let a = {
  foo: 1,
  bar: () => console.log(this.foo)
}

a.bar()  //undefined
Copy the code

In the above code, the arrow function this does not refer to the object A. Object A does not constitute a scope, so if you go up to the global scope, this points to the global scope. If we use the normal function definition method, the output is as expected, because the a.bar() function executes scoped to the A object.

let a = {
  foo: 1,
  bar: function() { console.log(this.foo) }
}

a.bar()  // 1Copy the code

Another mistake is to use arrow functions on prototypes, such as:

function A() {
  this.foo = 1
}

A.prototype.bar = () => console.log(this.foo)

let a = new A()
a.bar()  //undefinedCopy the code

Similarly, instead of pointing to A, this in the arrow function is traced back to the global scope according to the variable lookup rule. Similarly, there is no problem with ordinary functions.

From the above, we can see that the arrow function really has nothing except the parameters passed in! If you refer to variables other than this, arguments, or arguments in an arrow function, they must not be contained in the arrow function itself, but inherited from the parent scope.

When should arrow functions be used

Here, we can find that the arrow function is not a panacea, a little careless will step on the pit.

You Don’t Know About JS gives a decision graph for when to use arrow functions:

The above decision graph looks a little complicated, but I think three things are important:

  1. Arrow functions are suitable for pure function scenarios with no complex logic or side effects, such as inmap,reduce,filterIn the callback function definition of;
  2. Do not define arrow functions in the outermost layer, because the operation is inside the functionthisIt’s easy to contaminate the global scope. At least a layer of ordinary functions around the arrow function willthisKeep within sight;
  3. As mentioned at the beginning, the most attractive aspect of the arrow function is its brevity. In the case of multi-layer nested functions, the simplicity of the arrow function is not greatly improved, but affects the identification of the scope of the function. In this case, the arrow function is not recommended.