​箭头函数和普通函数有什么区别?

78 阅读5分钟

箭头函数和普通函数在多个方面存在区别,包括语法、返回值、作用域、参数传递、构造函数、原型、递归、arguments 对象、this 值、使用场景和函数名称等方面。

在使用函数时应根据具体需求选择合适的类型。

一、语法

箭头函数使用箭头(=>)来定义,而普通函数使用 function 关键字来定义。例如,一个简单的箭头函数可以写成这样:

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

而相应的普通函数写法如下:

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

二、返回值

如果箭头函数只有一个表达式,它将自动返回该表达式的结果,而无需使用 return 关键字。例如,上面的 add 函数可以写成这样:

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

普通函数必须使用 return 关键字来返回值。例如,相应的 add 函数将如下所示:

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

三、作用域

箭头函数和普通函数的作用域也不同。箭头函数没有自己的 this 值,而是继承了它们的父级作用域的 this 值。这意味着,在箭头函数内部使用 this 关键字时,它将指向其定义时的外部作用域。例如:

const person = {
  name: 'Alice',
  sayName: function() {
    console.log(this.name);
  },
  sayNameArrow: () => {
    console.log(this.name);
  }
};

person.sayName(); // "Alice"
person.sayNameArrow(); // undefined

在上面的例子中,sayName 方法是一个普通函数,sayNameArrow 方法是一个箭头函数。在 sayName 方法中,this 关键字指向 person 对象,因为它是通过对象调用的。但是在 sayNameArrow 方法中,this 关键字指向其定义时的外部作用域,因此它的值是 undefined

四、参数传递

箭头函数和普通函数在参数传递方面也有所不同。在普通函数中,可以使用 arguments 对象来访问函数的所有参数。但是在箭头函数中,arguments 对象是不可用的。如果要使用箭头函数访问参数,则需要将它们作为参数显式传递。例如:

function sum() {
  return Array.prototype.reduce.call(arguments, (a, b) => a + b);
}

sum(1, 2, 3); // 6

在上面的例子中,sum 函数使用 arguments 对象来访问所有参数。但是在箭头函数中,无法访问 arguments 对象,因此需要使用 Array.prototype.reduce.call 来传递参数。

五、构造函数

箭头函数不能用作构造函数,因为它们没有自己的 this 值。如果尝试在箭头函数中使用 new 关键字创建一个实例,则会抛出错误。例如:

const Person = (name) => {
  this.name = name;
};

const person = new Person('Alice'); // TypeError: Person is not a constructor

在上面的例子中,Person 函数是一个箭头函数,但是尝试使用 new 关键字创建一个实例会抛出错误。

六、原型

箭头函数没有自己的原型对象,也不能使用 new 关键字来创建对象。由于箭头函数没有原型,因此不能向其添加方法。例如:

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

add.prototype; // undefined

const obj = new add(); // TypeError: add is not a constructor

在上面的例子中,add 函数是一个箭头函数,因此无法访问其原型对象,也不能使用 new 关键字来创建对象。

七、递归

由于箭头函数没有自己的 this 值,因此不能在函数内部递归调用自身。如果尝试在箭头函数中递归调用自身,则会出现错误。例如:

const countdown = (n) => {
  if (n > 0) {
    console.log(n);
    countdown(n - 1);
  }
};

countdown(3); // TypeError: countdown is not a function

在上面的例子中,countdown 函数是一个箭头函数,尝试在函数内部递归调用自身时会抛出错误。

八、arguments 对象

在箭头函数中,没有自己的 arguments 对象,这意味着无法通过 arguments 对象来访问函数参数的列表。如果需要访问函数参数,必须使用命名参数或使用 ...rest 语法来获取剩余参数列表。例如:

const sum = (...args) => {
  return args.reduce((a, b) => a + b, 0);
}

console.log(sum(1, 2, 3)); // 6

在上面的例子中,sum 函数使用 ...rest 语法来获取所有的参数列表,然后使用 reduce 方法来计算它们的总和。

九、this 值

在普通函数中,this 值是根据函数的执行环境动态确定的,而在箭头函数中,this 值是在函数定义时确定的,而不是在函数执行时确定的。箭头函数的 this 值指向其父级作用域的 this 值,而不是函数自己的 this 值。例如:

const obj = {
  name: 'Alice',
  greet() {
    setTimeout(() => {
      console.log(`Hello, ${this.name}!`);
    }, 1000);
  }
};

obj.greet(); // Hello, Alice!

在上面的例子中,obj 对象中的 greet 方法是一个箭头函数,它使用 setTimeout 方法来异步执行一段代码,其中的箭头函数使用了父级作用域的 this 值,因此可以访问 obj 对象的 name 属性。

十、使用场景

箭头函数通常用于简化函数表达式的语法和提高代码可读性,特别是在使用回调函数时更为常见。普通函数则适用于更多的场景,例如在构造函数中创建对象,定义方法,定义具有多个参数的函数等。此外,由于箭头函数没有 this 值和自己的 arguments 对象,因此在需要访问这些对象的情况下,应使用普通函数。

十一、函数名称

箭头函数没有自己的名称,因此在调用堆栈跟踪中可能不太容易识别。如果需要给函数命名,应使用普通函数,或者在箭头函数外部使用变量来存储函数。例如:

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

console.log(add.name); // ""

在上面的例子中,add 函数是一个箭头函数,没有自己的名称,因此无法通过 name 属性获取函数名称。