The difference between arrow functions and normal functions in ES6

In ES6 (ECMAScript 6), the arrow function is a new function definition method, which has some special syntax and behavior compared with the traditional function definition method. In this article, we will discuss the difference between arrow functions and ordinary functions in ES6, explore their respective advantages and disadvantages, and applicable scenarios.

1. The basic syntax of arrow functions and ordinary functions

In ES6, the arrow function is defined by the "=>" symbol, and its basic syntax format is as follows:

(parameters) => expression

Among them, parameters represent the parameters of the function, which can be one or more parameters, and multiple parameters are separated by commas; expression represents the return value of the function. If the expression has only one line, the return keyword and curly braces can be omitted. For example, here's a simple example of an arrow function:

const add = (a, b) => a + b;

This arrow function takes two arguments a and b and returns their sum. The sum of any two numbers can be calculated by calling the add function:

const result = add(3, 5); // result = 8

In contrast, the traditional way of defining a function is to use the function keyword, and its basic syntax is as follows:

function functionName(parameters) {
  // function body
  return expression;
}

Among them, functionName is the name of the function, parameters are the parameters of the function, which can be one or more parameters, and multiple parameters are separated by commas; function body is the implementation code of the function, which can contain multiple lines of statements; the return keyword indicates the function The return value can be omitted. For example, here's a simple example of an ordinary function:

function add(a, b) {
  return a + b;
}

This ordinary function implements the same function as the previous arrow function, accepting two parameters a and b, and returning their sum. The sum of any two numbers can be calculated by calling the add function:

const result = add(3, 5); // result = 8

2. The difference between arrow function and ordinary function

Although both arrow functions and normal functions can be used to define functions, there are some important differences between them. In this section, we discuss these differences, including function context, the this keyword, the arguments object, default parameters, and constructors.

2.1 Function context

In ordinary functions, the function context is dynamically bound, which is determined by how the function is called. For example, in the following code, the context of the function greeting is the window object, because it is called through the window object:

function greeting() {
  console.log("Hello, " + this.name + "!");
}

const person = {
  name: "Alice",
  sayHello: greeting,
};

person.sayHello(); // output: "Hello, Alice!"

In this example, when person.sayHello calls the greeting function, the context of the function is bound to the person object, so the this keyword points to the person object, and the output is "Hello, Alice!". If the greeting function is called directly, the context is the global object window, so the this keyword points to the window object, and the output is "Hello, undefined!".

In contrast, the context of an arrow function is statically bound, which is determined at function definition time. For example, in the following code, the context of the arrow function greeting is the person object because it is defined in the person object:

const person = {
  name: "Alice",
  sayHello: () => {
    console.log("Hello, " + this.name + "!");
  },
};

person.sayHello(); // output: "Hello, undefined!"

In this example, person.sayHello calls the arrow function greeting. Although the this keyword is used inside the arrow function, the this keyword points to the context when the arrow function is defined, that is, the global object window, so the output is "Hello, undefined !". This means that arrow functions cannot be used as constructors, because they do not have their own this keyword, but instead inherit the parent context's this keyword.

2.2 this keyword

In normal functions, the this keyword refers to the caller of the function. For example, in the following code, the this keyword of the function greeting points to the person object because it is called through the person object:

function greeting() {
  console.log("Hello, " + this.name + "!");
}

const person = {
  name: "Alice",
  sayHello: greeting,
};

person.sayHello(); // output: "Hello, Alice!"

In this example, person.sayHello calls the greeting function, so the this keyword of the function points to the person object, and the output is "Hello, Alice!".

In contrast, the this keyword of an arrow function refers to the context in which the function was defined, not the context in which it was called. For example, in the following code, the this keyword of the arrow function greeting points to the person object because it is defined in the person object:

const person = {
  name: "Alice",
  sayHello: () => {
    console.log("Hello, " + this.name + "!");
  },
};

person.sayHello(); // output: "Hello, undefined!"

In this example, person.sayHello calls the arrow function greeting. Although the this keyword is used inside the arrow function, the this keyword points to the context when the arrow function is defined, that is, the global object window, so the output is "Hello, undefined !".

2.3 arguments object

In normal functions, the arguments object is a special object that contains all the parameters passed when the function is called. For example, in the following code, the function greeting takes a parameter name and outputs a greeting:

function greeting(name) {
  console.log("Hello, " + name + "!");
  console.log(arguments);
}

greeting("Alice", 23); // output: "Hello, Alice!", [ "Alice

In this example, the greeting function accepts a parameter name, and outputs the greeting "Hello, Alice!" through console.log, and at the same time outputs the arguments object, which contains all the parameters passed when the function is called, and the output result is [" Alice", 23].

In contrast, arrow functions do not have their own arguments object. If you need to access the parameters passed to the arrow function, you can use the rest parameter syntax. For example, in the following code, the arrow function greeting takes a rest parameter names and outputs a greeting:

const greeting = (...names) => {
  console.log("Hello, " + names.join(", ") + "!");
};

greeting("Alice", "Bob", "Charlie"); // output: "Hello, Alice, Bob, Charlie!"

In this example, the arrow function greeting uses the rest argument syntax, which accepts any number of arguments and passes them to the arrow function as an array of names. The arrow function internally uses console.log to output the greeting "Hello, Alice, Bob, Charlie!", where names.join(", ") separates the elements in the names array into a string with commas.

2.4 return value

In ordinary functions, functions can return a value through the return statement. For example, in the following code, the function sum takes two arguments a and b and returns their sum:

function sum(a, b) {
  return a + b;
}

const result = sum(1, 2); // result = 3

In this example, the function sum takes two parameters a and b and returns their sum via the return statement. When the sum function is called, it returns 3 and assigns the result to the variable result.

In contrast, arrow functions can automatically return a value without using a return statement. For example, in the following code, the arrow function sum takes two arguments, a and b, and returns their sum:

const sum = (a, b) => a + b;

const result = sum(1, 2); // result = 3

In this example, the arrow function sum takes two arguments a and b and returns their sum via the arrow function's implicit return. When the sum function is called, it returns 3 and assigns the result to the variable result.

2.5 Binding this keyword

In ordinary functions, the binding of the this keyword can be modified by the call, apply, and bind methods. For example, in the following code, the this keyword of the function greeting is bound to the person object and invoked through the call method:

function greeting() {
  console.log("Hello, " + this.name + "!");
}

const person = {
  name: "Alice",
};

greeting.call(person); // output: "Hello, Alice!"

In this example, the call method accepts a parameter, which is bound to the this keyword inside the function. When calling greeting.call(person), the this keyword of the function greeting is bound to the person object, and the output is "Hello, Alice!".

In contrast, the this keyword of an arrow function is statically bound, and it always points to the this value when the function is defined, not the this value when it is called. For example, in the following code, the this keyword of the arrow function greeting points to the this value of the outer scope:

const person = {
  name: "Alice",
  greet: function () {
    const greeting = () => {
      console.log("Hello, " + this.name + "!");
    };

    greeting();
  },
};

person.greet(); // output: "Hello, Alice!"

In this example, the person object defines a greet method, which contains an arrow function greeting. When calling the greeting function, its this keyword points to the person object because it is defined inside the person object. Therefore, when person.greet() is called, the output is "Hello, Alice!".

2.6 does not support arguments.callee and new.target

In normal functions, arguments.callee is a pointer to the currently executing function. This feature has been deprecated, but still exists in some legacy code. For example, in the following code, the function fibonacci uses arguments.callee to call itself recursively:

function fibonacci(n) {
  if (n <= 1) {
    return n;
  }

  return arguments.callee(n - 1) + arguments.callee(n - 2);
}

fibonacci(10); // output: 55

In this example, the function fibonacci uses arguments.callee to call itself recursively until the 10th term of the Fibonacci sequence is calculated. When calling fibonacci(10), the output is 55.

Arrow functions, by contrast, do not support arguments.callee because they do not have their own arguments object. If you need to call an arrow function recursively, you can use a named function expression or define a function externally to call it recursively. For example, in the following code, the named function expression fibonacci uses arguments.callee to call itself recursively:

const fibonacci = function fib(n) {
  if (n <= 1) {
    return n;
  }

  return fib(n - 1) + fib(n - 2);
};

fibonacci(10); // output: 55

In this example, the named function expression fibonacci is assigned to the variable fib, which can be used inside the function to recursively call itself. When calling fibonacci(10), the output is 55.

Another unsupported feature is new.target, which is used to determine whether a constructor is invoked with the new keyword. In normal functions, you can use new.target to check that the constructor was called correctly. For example, in the following code, the function Person uses new.target to check whether it was called with the new keyword:

function Person(name) {
  if (new.target === undefined) {
    throw new Error("Constructor must be called with new keyword");
  }

  this.name = name;
}

const alice = new Person("Alice"); // ok
const bob = Person("Bob"); // error: Constructor must be called with new keyword

In this example, the function Person uses new.target to check if it was called with the new keyword. If not called with the new keyword, it will throw an error. When calling new Person("Alice"), a new Person object is created and passed the parameter "Alice". When calling Person("Bob"), it doesn't use the new keyword, so it throws an error.

In contrast, arrow functions do not support new.target because they cannot be used as constructors. If you need to create an object using the new keyword, you must use a normal function. For example, in the following code, an ordinary function is used to create an object:

function Person(name) {
  this.name = name;
}

const alice = new Person("Alice"); // ok
const bob = Person("Bob"); // error: Cannot set property 'name' of undefined

In this example, use the ordinary function Person to create an object. When calling new Person("Alice"), a new Person object is created and passed the parameter "Alice". When calling Person("Bob"), it doesn't use the new keyword, so it throws an error.

2.7 Summary

Arrow functions are a new type of function in ES6, which has many features and advantages. Compared with ordinary functions, arrow functions have shorter syntax, more concise code, clearer context, simpler this binding and better performance. However, arrow functions also have some limitations and disadvantages, such as not being able to act as a constructor, not having an arguments object, not being able to use the yield keyword, not being able to access arguments.callee and new.target, etc.

In actual programming, you need to choose to use arrow functions or ordinary functions according to the specific situation. If you need to access the this keyword or the arguments object, or use the yield keyword to implement asynchronous programming, it is recommended to use ordinary functions. Arrow functions are recommended if the code needs to be more concise and clear, and does not require access to the this keyword or the arguments object.

Guess you like

Origin blog.csdn.net/tyxjolin/article/details/130486038