JavaScript 箭头函数与普通函数区别

0 阅读7分钟

JavaScript 箭头函数与普通函数区别

1. 概述

在 JavaScript 开发中,箭头函数和普通函数是我们经常使用的两种函数定义方式。我将在这篇文章中为大家详细讲解它们的区别,帮助大家在实际开发中正确选择和使用它们。

我将从最基础的概念开始,逐步深入到实际应用,让大家全面理解这两种函数的特性。

2. 普通函数和箭头函数是什么

2.1 this 是什么

在介绍普通函数和箭头函数之前,我先简要介绍一下 this 是什么。

this 是 JavaScript 中的一个关键字,指向当前执行上下文的对象。在不同场景下,this 的指向不同:

  • 方法调用:指向调用该方法的对象

    const obj = { name: 'Tom', getName() { return this.name; } };
    
  • 普通函数调用:指向全局对象(非严格模式)或 undefined(严格模式)

    function fn() { return this; }
    console.log(fn());  // 非严格模式输出: window, 严格模式输出: undefined
    
  • 构造函数:指向新创建的实例对象

    function Person(name) { this.name = name; }
    
  • call/apply/bind:指向第一个参数指定的对象

    fn.call(obj);  // this 指向 obj
    

2.2 arguments 是什么

arguments 是一个类数组对象,包含了函数调用时传入的所有参数。

function sum(a, b) {
  console.log(arguments[0]);  // 第一个参数
  console.log(arguments.length);  // 参数个数
  return a + b;
}
sum(1, 2, 3);  // 即使函数只声明了2个参数,也能访问第3个

2.3 构造函数是什么

构造函数是通过 new 关键字调用的函数,用于创建对象实例。

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

const tom = new Person('Tom', 20);  // 创建新实例
console.log(tom.name);  // 输出: Tom

2.4 prototype 是什么

prototype 是函数的一个属性,用于实现继承。通过 prototype,可以为所有实例共享方法。

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

Person.prototype.sayHello = function() {
  console.log('Hello, I am ' + this.name);
};

const tom = new Person('Tom');
tom.sayHello();  // 输出: Hello, I am Tom

2.5 普通函数

使用 function 关键字定义,可通过函数声明或函数表达式创建。

定义方式

// 函数声明
function add(a, b) {
  return a + b;
}

// 函数表达式
const add = function(a, b) {
  return a + b;
};

主要特点

  • 函数声明会提升到作用域顶部:函数可以在定义之前调用

    sayHello();  // ✅ 可以在定义之前调用
    
    function sayHello() {
      console.log('Hello!');
    }
    

    函数表达式和箭头函数不会提升,必须在定义之后调用

  • 有自己的 this 绑定

  • 可作为构造函数

  • arguments 对象

2.6 箭头函数

ES6 引入的简写语法,使用 => 符号,本质上是匿名函数表达式。

定义方式

// 无参数
const greet = () => console.log('Hello!');

// 单参数
const square = num => num * num;

// 多参数
const add = (a, b) => a + b;

// 多行函数体
const calc = (a, b) => {
  const sum = a + b;
  return sum * 2;
};

主要特点

  • 语法简洁
  • this 继承外层作用域(词法绑定)
    const obj = {
      name: 'Tom',
      getName: function() {
        const fn = () => this.name;  // this 继承外层的 obj
        return fn();
      }
    };
    
  • 不能作为构造函数
  • 没有 arguments 对象

2.7 对比

特性普通函数箭头函数
定义方式function=>
this 绑定动态绑定词法绑定
构造函数可以不可以
arguments
prototype
函数提升

3. 语法区别

接下来,我将为大家详细讲解箭头函数和普通函数在语法上的区别。

3.1 定义方式

普通函数

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

箭头函数

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

3.2 箭头函数的简写规则

  • 单参数:可省略括号

    const square = num => num * num;
    
  • 无参数:必须保留括号

    const greet = () => console.log('Hello!');
    
  • 单行返回:省略 {}return

    const add = (a, b) => a + b;
    
  • 多行函数体:需要 {} 和显式 return

    const add = (a, b) => {
      const sum = a + b;
      return sum;
    };
    
  • 返回对象:用括号包裹

    const getObj = () => ({ name: 'Tom' });
    

3.3 函数名

  • 普通函数:可以命名或匿名
  • 箭头函数:都是匿名函数(赋值给变量后通过变量名调用)

4. 详细对比(包含 this、arguments、构造函数、prototype 的详细差异)

在这一章中,我将深入对比箭头函数和普通函数在 this 绑定、arguments 对象、构造函数能力和 prototype 属性方面的差异,帮助大家更深入地理解它们的本质区别。

4.1 this 绑定差异

普通函数:动态绑定,this 指向调用该函数的对象

const obj = {
  name: 'Tom',
  fn: function() {
    console.log(this.name);  // 输出: Tom
  }
};
obj.fn();

箭头函数:词法绑定,this 继承外层作用域

const obj = {
  name: 'Tom',
  fn: () => {
    console.log(this.name);  // 输出: window.name (或 undefined)
  }
};
obj.fn();

箭头函数无法通过 call/apply/bind 改变 this

const fn = () => console.log(this);
fn.call({ name: 'Tom' });  // this 仍指向外层作用域

4.2 arguments 对象差异

普通函数:有 arguments 对象

function sum() {
  console.log(arguments.length);  // 输出: 3
}
sum(1, 2, 3);

箭头函数:没有 arguments 对象,使用剩余参数 ...args

const sum = (...args) => {
  console.log(args.length);  // 输出: 3
};
sum(1, 2, 3);

4.3 构造函数能力差异

普通函数:可作为构造函数

function Person(name) {
  this.name = name;
}
const tom = new Person('Tom');  // ✅ 可以

箭头函数:不能作为构造函数

const Person = (name) => {
  this.name = name;
};
const tom = new Person('Tom');  // ❌ 报错

4.4 prototype 差异

普通函数:有 prototype 属性

function Person() {}
Person.prototype.sayHello = function() {
  console.log('Hello');
};

箭头函数:没有 prototype 属性

const Person = () => {};
Person.prototype.sayHello = function() {};  // ❌ 报错

5. 返回值处理

箭头函数和普通函数在返回值处理上也有显著差异。我将为大家介绍显式返回和隐式返回的区别,以及箭头函数在返回对象时的特殊处理方式。

5.1 显式返回 vs 隐式返回

普通函数:必须显式使用 return 语句

function add(a, b) {
  return a + b;  // 必须显式 return
}

箭头函数:单行表达式可隐式返回

const add = (a, b) => a + b;  // 隐式返回表达式结果

5.2 箭头函数的返回规则

单行返回:省略 {}return

const square = num => num * num;  // 自动返回 num * num

多行函数体:需要 {} 和显式 return

const add = (a, b) => {
  const sum = a + b;
  return sum;  // 必须显式 return
};

无返回值:返回 undefined

const log = msg => {
  console.log(msg);  // 没有 return,返回 undefined
};

5.3 返回对象

箭头函数返回对象需用括号包裹

// ❌ 错误:花括号会被误认为是函数体
const getUser = () => { name: 'Tom' };

// ✅ 正确:用括号包裹对象
const getUser = () => ({ name: 'Tom' });

普通函数返回对象无需特殊处理

function getUser() {
  return { name: 'Tom' };  // 直接返回
}

6. 总结

经过前面的学习,相信大家对箭头函数和普通函数已经有了全面的了解。在这里,我将总结它们的核心差异,并为大家提供在实际开发中如何选择的建议。

6.1 核心差异总结

特性普通函数箭头函数
语法function 关键字=> 符号
this 绑定动态绑定(调用时确定)词法绑定(继承外层)
arguments无(用 ...args 替代)
构造函数可以不可以
prototype
函数提升是(仅函数声明)
返回值需显式 return可隐式返回

6.2 如何选择

使用箭头函数的场景

  • 简单的回调函数(如 mapfilterreduce
  • 需要保留外层 this 的场景
  • 定时器回调
  • 简短的单行函数
// ✅ 适合箭头函数
arr.map(item => item * 2);
setInterval(() => console.log('tick'), 1000);

使用普通函数的场景

  • 作为构造函数
  • 需要使用 arguments 对象
  • 对象方法(需要访问对象自身的 this
  • 需要函数提升
  • 作为生成器函数(使用 yield
// ✅ 适合普通函数
function Person(name) {
  this.name = name;
}

const obj = {
  name: 'Tom',
  sayHello() {
    console.log(this.name);
  }
};

6.3 关键要点

  1. this 是最核心的差异:理解箭头函数的 this 继承规则是关键
  2. 语法简洁但有限制:箭头函数简洁但不适合所有场景
  3. 构造函数只能用普通函数:箭头函数无法通过 new 调用
  4. 对象方法慎用箭头函数:容易导致 this 指向错误

6.4 最佳实践

  • 优先使用箭头函数:在回调、函数式编程场景中
  • 必要时使用普通函数:构造函数、对象方法等
  • 保持代码可读性:复杂逻辑不要过度使用箭头函数简写
  • 理解本质:明确选择箭头函数或普通函数的原因