5 differences between arrow functions and regular functions

Posted May 26, 20207 min read

Author:Dmitri Pavlutin

Translation:crazy technical house

Original: https://dmitripavlutin.com/di...

In JavaScript, you can define functions in many ways.

The first common method is to use the keyword function:

//function declaration
function greet(who) {
  return `Hello, ${who}!`;
}
//Function expression
const greet = function(who) {
  return `Hello, ${who}`;
}

Function declarations and function expressions in the code are called "regular functions".

Starting from ES2015, the second available method is arrow function syntax:

const greet =(who) => {
  return `Hello, ${who}!`;
}

Although the syntax of both can define functions, how do you choose when developing? This is a good question.

In this article, I will show the main differences between the two so that you can choose the correct grammar according to your needs.

1 . This value

1.1 Regular functions

Inside regular JavaScript functions, the this value(that is, the execution context) is dynamic.

The dynamic context means that the value of this depends on how the function is called. In JavaScript, there are 4 ways to call regular functions.

During the simple call, the value of this is equal to the global object( undefined if the function is running in strict mode):

function myFunction() {
  console.log(this);
}

//simple call
myFunction(); //logs global object(window)

During method invocation, the value of this is the object that owns the method:

const myObject = {
  method() {
    console.log(this);
  }
};
//method call
myObject.method(); //logs "myObject"

In indirect calls using myFunc.call(context, arg1, ..., argN) or myFunc.apply(context, [arg1, ..., argN]), the value of this is equal to Parameters:

function myFunction() {
  console.log(this);
}

const myContext = {value:'A'};

myFunction.call(myContext); //logs {value:'A'}
myFunction.apply(myContext); //logs {value:'A'}

During a constructor call with the keyword new, this is equal to the newly created instance:

function MyFunction() {
  console.log(this);
}

new MyFunction(); //logs an instance of MyFunction

1.2 Arrow functions

The behavior of this in arrow functions is very different from that of regular functions.

No matter how or where it is executed, the value of this inside the arrow function is always equal to the value of this of the external function. In other words, the arrow function can parse lexically this, and the arrow function does not define its own execution context.

In the following example, myMethod() is an external function of the arrow function callback():

const myObject = {
  myMethod(items) {
    console.log(this); //logs "myObject"
    const callback =() => {
      console.log(this); //logs "myObject"
    };
    items.forEach(callback);
  }
};

myObject.myMethod([1, 2, 3]);

The value of this in the arrow functioncallback()is equal to this of the external function myMethod().

The lexical analysis of this is one of the important functions of arrow functions. When using callbacks inside methods, make sure that the arrow function does not define its own this:there is no longer a solution to const self = this or callback.bind(this).

2. Constructor

2.1 General functions

As mentioned in the previous section, regular functions can easily construct objects.

For example, use the Car() function to create an instance of a car:

function Car(color) {
  this.color = color;
}

const redCar = new Car('red');
redCar instanceof Car; //=> true

Car is a regular function, and a new instance of type Car will be created when called with the keyword new.

2.2 Arrow functions

The lexical this solves that arrow functions cannot be used as constructors.

If you try to call an arrow function prefixed with the new keyword, JavaScript will throw an error:

const Car =(color) => {
  this.color = color;
};

const redCar = new Car('red'); //TypeError:Car is not a constructor

Calling new Car('red')(where Car is an arrow function) will throw TypeError:Car is not a constructor.

3 . Arguments object

3.1 General functions

Inside the body of a regular function, arguments is a special array-like object, which contains the parameter list of the called function.

Let's call the myFunction function with 3 parameters:

function myFunction() {
  console.log(arguments);
}

myFunction('a', 'b'); //logs {0:'a', 1:'b'}

Arguments, similar to array objects, contain call parameters:'a' and' b'.

3.2 Arrow functions

On the other hand, the arrow function has no special keywords defined within arguments.

Analyze the arguments object lexically:the arrow function accesses arguments from external functions

Let's try to access arguments inside the arrow function:

function myRegularFunction() {
  const myArrowFunction =() => {
      console.log(arguments);
  }
  myArrowFunction('c', 'd');
}

myRegularFunction('a', 'b'); //logs {0:'a', 1:'b'}

The arrow function myArrowFunction() is called by the parameters 'c',' d'. Inside its body, the arguments object is equal to the parameters that callmyRegularFunction(): 'a', ' b'.

If you want to access the direct parameters of the arrow function, you can use the remaining parameters ... args:

function myRegularFunction() {
  const myArrowFunction =(... args) => {
      console.log(args);
  }
  myArrowFunction('c', 'd');
}

myRegularFunction('a', 'b'); //logs {0:'c', 1:'d'}

The remaining parameters ... args accept the execution parameters of the arrow function:{0:'c', 1:'d'}.

4. Implicit return

4.1 Regular functions

Use the return expression statement to return the result from the function:

function myFunction() {
  return 42;
}

myFunction(); //=> 42

If the return statement is missing, or there is no expression after the return statement, the regular function implicitly returns undefined:

function myEmptyFunction() {
  42;
}

function myEmptyFunction2() {
  42;
  return;
}

myEmptyFunction(); //=> undefined
myEmptyFunction2(); //=> undefined

4.2 Arrow functions

You can return values from arrow functions in the same way as regular functions, with one useful exception.

If the arrow function contains an expression, and you omit the curly braces of the function, the expression is returned explicitly. These are inline arrow functions

const increment =(num) => num + 1;

increment(41); //=> 42

increment() contains only one expression:num + 1. This expression is implicitly returned by the arrow function without using the return keyword.

5 . Method

5.1 General functions

Regular functions are a common way to define methods on classes.

In the following Hero class, the regular function definition methodlogName()is used:

class Hero {
  constructor(heroName) {
    this.heroName = heroName;
  }

  logName() {console.log(this.heroName);}}

const batman = new Hero('Batman');

Normal functions are usually used as methods.

Sometimes you need to provide this method as a callback to setTimeout() or event listener. In this case, you may have difficulty accessing the value of this.

For example, use the logName() method as the callback for setTimeout():

setTimeout(batman.logName, 1000);
//after 1 second logs "undefined"

After 1 second, undefined will be output to the console. setTimeout() performs a simple call to logName(where this is a global object). At this time, the method will be separated from the object.

Let's manually bind the this value to the correct context:

setTimeout(batman.logName.bind(batman), 1000);
//after 1 second logs "Batman"

batman.logName.bind(batman) binds this value to batman instance. Now, you can be sure that the method will not lose context.

Manually binding this requires boilerplate code, especially if you have many methods. There is a better way:use arrow functions as class fields.

5.2 Arrow functions

Thanks to Class Field Proposal (currently in phase 3), you can use arrow functions as methods in classes.

Contrary to regular functions, the methods defined with arrows can now bind this lexicals to class instances.

Let's use the arrow function as a field:

class Hero {
  constructor(heroName) {
    this.heroName = heroName;
  }

  logName =() => {
      console.log(this.heroName);
  }
}

const batman = new Hero('Batman');

Now, you can use batman.logName for callbacks without manually binding this. The value of this in thelogName()method is always a class instance:

setTimeout(batman.logName, 1000);
//after 1 second logs "Batman"

6 . Summary

Understanding the differences between regular functions and arrow functions can help you choose the right syntax for specific needs.

The this value in regular functions is dynamic and depends on how it is called. The this in the arrow function is lexically bound, equal to the this of the external function.

The arguments object in regular functions contains a list of parameters. In contrast, arrow functions do not define arguments(but you can easily access arrow function parameters with the remaining arguments ... args)

If the arrow function has an expression, the expression will be returned implicitly even without the return keyword.

Finally, you can use arrow function syntax to define methods inside the class. The thick arrow method binds the this value to the class instance.

No matter how the fat arrow method is called, this is always equal to the class instance, which is very useful when calling back these methods.


This article is the first WeChat public account:front-end pioneer

Welcome to scan the QR code to pay attention to the public number, and push you fresh front-end technical articles every day

Welcome to scan the QR code to follow the public number, and push you fresh front-end technical articles every day


Welcome to continue reading other highly praised articles in this column: