22

Preface

starting address , welcome to comment, point out the deficiencies of this article.

In JavaScript, we can define functions in many ways, such as: function declarations, function expressions, and arrow functions

<!--truncate-->

// 函数声明
function normalFn() {
    return 'normalFn';
}
// 函数表达式
const normalFn = function() {
    return 'normalFn';
}
// 箭头函数
const arrowFn = () => {
    return 'arrowFn';
}

Among them, the arrow function is ES2015 (ES6) standard, and its syntax is different from the two definition methods of function declaration and function expression before ES6. In this article, the two definitions of function declaration and function expression are classified as ordinary functions.

So, what is the difference between a normal function and an arrow function? 🤔️

1. this points to

In JavaScript, the this is a basic and important knowledge point.

1.1 Ordinary functions

In ordinary functions, this is dynamic, and its value depends on how the function is called. There are usually the following four calling methods:

  • 1) When calling directly, it points to the global object (undefined in strict mode)
function fnc() {
    console.log(this);
}

fnc(); // 全局对象(global 或 window)
  • 2) When the method is called, it points to the object that called the method
var obj = {
    fnc1: function(){
        console.log(this === obj);
    }
}
obj.fnc2 = function() {
    console.log(this === obj);
}
function fnc3() {
    console.log(this === obj);
}
obj.fnc3 = fnc3;
obj.fnc1(); // true
obj.fnc2(); // true
obj.fnc3(); // true
  • 3) When new is called, it points to the newly created instance object
function fnc() {
  console.log(this);
}

new fnc(); // fnc 的实例 fnc {}
  • 4) When call, apply, and bind are called, they point to the first parameter of the three methods
function fnc() {
  console.log(this);
}

const ctx = { value: 'a' };

fnc.call(ctx);      // { value: 'a' }
fnc.apply(ctx);     // { value: 'a' }
fnc.bind(ctx)();    // { value: 'a' }

In the old version of JavaScript, bind is often used to explicitly set the point of this. This mode can usually be found in some early versions of the framework (such as React) before the emergence of ES6. The emergence of arrow functions provides a more convenient way to solve this problem.

1.2 Arrow function

No matter where it is executed or where it is executed, the value of this inside the arrow function is always equal to the value of the outer function, that is, the arrow function does not change the direction of this.

const obj = {
  fnc(arr) {
    console.log(this); // obj
    const cb = () => {
      console.log(this); // obj
    };
    arr.forEach(cb);
  }
};

obj.fnc([1, 2, 3]); 

Note: Since the arrow function does not have its own this pointer , when called by the call(), apply() and bind() methods, only parameters can be passed, and this cannot be bound, and their first parameter will be ignored. As follows: (The example comes from MDN )

var adder = {
  base : 1,

  add : function(a) {
    var f = v => v + this.base;
    return f(a);
  },

  addThruCall: function(a) {
    var f = v => v + this.base;
    var b = {
      base : 2
    };

    return f.call(b, a);
  }
};

console.log(adder.add(1));         // 输出 2
console.log(adder.addThruCall(1)); // 仍然输出 2

2. Constructor

In JavaScript, the inheritance of functions and classes is achieved through the prototype attribute, and prototype has the attribute constructor pointing to the constructor, as follows:

function fnc() {}
console.lof(fnc.prototype) // {constructor: ƒ}

When an arrow function is used to define a function, it does not have the prototype attribute, so it cannot point to the constructor.

const arrowFnc = () => {}

console.log(arrowFnc.prototype) // undefined

Regarding the difference between ordinary function and arrow function in the constructor, a question can be raised- arrow function can be instantiated by new?

const arrowFnc = () => {}
const arrowIns = new arrowFnc() // Uncaught TypeError: arrowFnc is not a constructor

The answer is [ cannot ], so why? 🤔️

  • Without own this, it means that apply, call, etc. cannot be called
  • There is no prototype attribute, and when the new command is executed, the prototype of the constructor function needs to be assigned to the new object's \_ proto_
function newOperator(Con, ...args) {
  let obj = {};
  Object.setPrototypeOf(obj, Con.prototype); // 相当于 obj.__proto__ = Con.prototype
  let result = Con.apply(obj, args);
  return result instanceof Object ? result : obj;
}

The more specific reason is that there are two internal methods of JavaScript function: [[Call]] and [[Construct]]. When the function is called directly, the [[Call]] method is executed, that is, the function body is executed directly, and new is called. When is the implementation of the [[Construct]] method. Arrow functions have no [[Construct]] method, so they cannot be instantiated as a constructor.

3. As a method attribute

'use strict';
var obj = {
  i: 10,
  b: () => console.log(this.i, this),
  c: function() {
    console.log(this.i, this)
  }
}
obj.b(); // undefined, Window{...}
obj.c(); // 10, Object {...}

As you can see, the arrow function is not bound by this, and its direction is always consistent with the previous level.

As mentioned above, when a constructor or class is called by new, its this points to the newly created instance object. It should be noted that this here is the this in the constructor, not anywhere in the function or class. As follows:

class Person {
  constructor(name) {
    this.name = name;
  }

  getName() {
    console.log(this.name, this);
  }
}

const p = new Person('Tom');

p.getName();                // Tom
setTimeout(p.getName, 100); // undefined, Window{...}

In order to avoid this kind of error, we usually need to bind this in the constructor, as shown below:

class Person {
  constructor(name) {
    this.name = name;
    this.getName = this.getName.bind(this);
  }

  getName() {
    console.log(this.name);
  }
}
const p = new Person('Tom');
setTimeout(p.getName, 100); // Tom

This way of writing is easy to find in React, but it is actually to fill the pitfalls of JavaScript. Of course, you can also use arrow functions to avoid this error and simplify the writing, as follows:

class Person {
  constructor(name) {
    this.name = name;
  }

  getName = () => {
    console.log(this.name);
  }
}
const p = new Person('Tom');
setTimeout(p.getName, 100); // Tom

When using arrow functions, this has lexical constraints, which means that the arrow function will automatically bind this to the context in which it is defined.

4. Parameters

The main difference between ordinary functions and arrow functions in terms of parameters is that arrow functions are not bound to the arguments object.

const fn = () => arguments[0];
fn(1); // Uncaught ReferenceError: arguments is not defined

When we need to use parameters, we can consider using the remaining parameters, as follows:

const fn = (...args) => args[0];
fn(1, 2); // 1

In addition, when the number of function parameters is 1, the arrow function can omit the parentheses and make abbreviations, as shown below:

const fn = x => x * x;

5. Return value

When processing the return value of a function, arrow functions can return implicitly compared to ordinary functions.

const sum = (a, b) => {
  return a + b
}
const sum = (a, b) => (a + b);

Implicit return usually creates a one-line operation for map, filter and other operations. Note: If the function theme cannot be written as a one-line code, the normal function body syntax must be used, that is, the curly braces and return cannot be omitted.

[1,2,3].map(i => i * 2); // [2,4,6]
[1,2,3].filter(i => i != 2); // [1,3]

to sum up

This article mainly introduces the difference between ordinary functions and arrow functions. Compared with ordinary functions, the main differences of ES6 arrow functions are as follows:

  • The arrow function is not bound to arguments , you can use ...args instead;
  • Arrow functions can return implicitly;
  • this in the arrow function is lexically bound and consistent with the outer function;
  • The arrow function does not have the prototype attribute, and cannot be instantiated with new, and cannot be bound to this through call, apply, etc.;
  • When the method class definition, the function does not need to arrow constructor binding this .
If this article is helpful to you, please like it 👍, if there is an error in this article, please comment and correct it 🤝.

Reference article


Chengbo
273 声望16 粉丝

厚于德,诚于信,敏于行!