箭头函数(Arrow Functions)和普通函数(Regular Functions)

在 JavaScript 中,箭头函数(Arrow Functions)和普通函数(Regular Functions)有以下主要区别:

1. 语法

  • 箭头函数 :使用 => 语法,更简洁,可省略 functionreturn(单行表达式时)。

    复制代码
    // 普通函数
    function add(a, b) { return a + b; }
    
    // 箭头函数
    const add = (a, b) => a + b;
  • 普通函数 :使用 function 关键字定义。

    复制代码
    function sayHello() {
      console.log("Hello");
    }

2. this 指向

  • 箭头函数

    • 不绑定 this,继承自父级作用域(定义时的上下文)。
    • 适合不需要自己上下文的场景(如回调函数)。
    复制代码
      const obj = {                      // 全局作用域(Global Scope)
        name: "Alice",                   // obj 对象作用域(Object Scope)
        greet: function() {              // greet 方法作用域(Function Scope)
          setTimeout(() => {             // 箭头函数作用域(Arrow Function Scope)
            console.log(`Hello, ${this.name}`);
          }, 1000);
        }
      };
      obj.greet();
    • 箭头函数的 this 继承自哪里?

      箭头函数的 this 继承自它被定义时的外层作用域的 this (即 greet 函数的 this),而 不是 继承外层函数(greet)本身。

      • greet 是一个普通函数,它的 this 由调用方式决定(obj.greet() 所以 this = obj)。

      • 箭头函数继承了 greetthis(即 obj),而不是 greet 函数对象。

    • 为什么不是指向 greet 函数?

      • 函数本身的 this 和函数对象是 完全不同的概念

      • greet 作为函数对象时,它的名字是 greet,但它的 this 是动态绑定的(这里是 obj)。

        const obj = {
        name: "Alice",
        greet: function() {
        setTimeout(() => {
        console.log(Hello, ${this.name}); // 继承自 greet() 的 this
        }, 1000);
        }
        };
        obj.greet(); // "Hello, Alice"

  • 普通函数

    • 绑定自己的 this ,指向调用该函数的对象(或全局对象 / 严格模式下为 undefined)。

      const obj = {
      name: "Bob",
      greet: function () {
      console.log(this.name); // "Bob"
      setTimeout(function () {
      console.log(Hello, ${this.name}); // this 指向全局对象或 undefined
      }, 1000);
      }
      };
      obj.greet(); // "Hello, undefined" 或报错

  • const/let 声明的全局变量 不会 成为全局对象(如 window)的属性。

  • 只有 var 声明的变量或直接赋值到 this 的属性(如 window.age = 20)才会被 this.age 访问到。

    var age = 20; // var 会挂载到 window
    /// const age = 20 //const 是局部变量
    const obj = {
    greet: function() {
    setTimeout(function() {
    console.log(this.age); // 20(this 指向 window)
    }, 1000);
    }
    };
    obj.greet();

3. arguments 对象

  • 箭头函数

    • 没有自己的 arguments 对象,继承自父级作用域。

      const sum = () => {
      console.log(arguments); // 报错或引用外层的 arguments
      };
      sum(1, 2); // 错误:arguments 未定义

  • 普通函数

    • 有自己的 arguments 对象,包含调用时的参数。

      function sum() {
      return arguments[0] + arguments[1];
      }
      sum(1, 2); // 3

4. 使用限制

  • 箭头函数

    • 不能使用 argumentssupernew.target

    • 不能使用 yield(不能用作生成器)。

    • 不能使用 new 实例化 (没有 [[Construct]] 方法)。

      const ArrowClass = () => {};
      new ArrowClass(); // 错误:箭头函数不能用作构造函数

  • 普通函数

    • 可以使用 argumentssupernew.targetyield

    • 可以使用 new 实例化(作为构造函数)。

      function RegularClass() {
      this.value = 42;
      }
      const instance = new RegularClass();

5. yieldawait

  • 箭头函数

    • 不能单独使用 yield(除非在生成器函数内部)。

    • 可以使用 await,但需要在 async 箭头函数中。

      const fetchData = async () => {
      const response = await fetch("api/data");
      return response.json();
      };

  • 普通函数

    • 可以使用 yield 定义生成器函数。

      function* generator() {
      yield 1;
      yield 2;
      }

6. 适用场景

  • 箭头函数

    • 简单的回调函数(如 mapfiltersetTimeout)。
    • 需要保留父级 this 的场景。
    • 单行逻辑的函数。
  • 普通函数

    • 构造函数(需要创建实例)。
    • 需要动态 this 绑定的场景(如方法、事件处理)。
    • 需要使用 arguments 对象的场景。

总结对比表

特性 箭头函数 普通函数
语法 简洁,使用 => 完整,使用 function
this 指向 继承自父级作用域 动态绑定(调用者)
arguments 对象
能否使用 new
能否使用 yield 否(除非在生成器内)
适用场景 回调函数、需要保留 this 构造函数、动态 this 绑定

如何理解this 指向 箭头函数的this继承自父级作用域 普通函数的this是动态绑定(调用者)

要理解 this 指向的差异,关键在于抓住箭头函数的 this 是 "静态绑定"(定义时确定),而普通函数的 this 是 "动态绑定"(调用时确定) 这一核心区别。下面通过具体场景和例子展开说明:

一、箭头函数:this 继承自父级作用域(定义时确定)

箭头函数没有自己的 this,它的 this 是在定义函数时 就固定好的,等于父级作用域(外层代码块)的 this,且永远不会改变。

可以理解为:箭头函数的 this 是 "抄" 父级的,一旦定义就 "锁死",后续无论怎么调用,this 都不会变。

例子 1:对象方法中的箭头函数
复制代码
const obj = {
  name: "Alice",
  // 普通函数作为方法(父级作用域)
  getParentThis: function() {
    // 箭头函数定义在 getParentThis 内部,父级作用域是 getParentThis 的 this
    const arrowFunc = () => {
      console.log(this.name); // this 继承自 getParentThis 的 this(即 obj)
    };
    return arrowFunc;
  }
};

const func = obj.getParentThis(); 
func(); // 输出 "Alice"(箭头函数的 this 是定义时的父级 this,即 obj)
  • 箭头函数 arrowFunc 定义在 getParentThis 内部,父级作用域的 thisobj(因为 getParentThisobj 调用的),所以箭头函数的 this 就是 obj
  • 即使后续用其他方式调用 func(如 func.call(otherObj)),this 也不会变,始终是 obj
例子 2:全局作用域中的箭头函数
复制代码
// 全局作用域的 this 是 window(浏览器环境)
const globalArrow = () => {
  console.log(this === window); // 输出 true(继承全局作用域的 this)
};

globalArrow(); 
const obj = { fn: globalArrow };
obj.fn(); // 依然输出 true(箭头函数的 this 不会因调用者变化而改变)

二、普通函数:this 是动态绑定(调用时确定,由调用者决定)

普通函数的 this 是在调用函数时 才确定的,取决于 "谁调用了它",即 "调用者"。调用方式不同,this 指向就可能不同。

常见的调用场景决定 this 指向的规则:

  1. 直接调用 (如 fn()):this 指向全局对象(浏览器中是 window,Node 中是 global;严格模式下为 undefined)。
  2. 作为对象方法调用 (如 obj.fn()):this 指向该对象(obj)。
  3. call/apply/bind 调用this 指向传入的第一个参数。
  4. 作为构造函数调用 (如 new Fn()):this 指向新创建的实例对象。
例子 1:不同调用方式下的普通函数 this
复制代码
function regularFunc() {
  console.log(this.name);
}

const obj1 = { name: "obj1" };
const obj2 = { name: "obj2" };

// 1. 直接调用:this 指向全局(无 name 属性,输出 undefined)
regularFunc(); // undefined

// 2. 作为对象方法调用:this 指向调用的对象
obj1.fn = regularFunc;
obj1.fn(); // 输出 "obj1"(this 是 obj1)

// 3. 用 call 强制绑定:this 指向传入的 obj2
regularFunc.call(obj2); // 输出 "obj2"
例子 2:对比箭头函数和普通函数在回调中的差异

最典型的场景是定时器回调:

复制代码
const person = {
  name: "Bob",
  // 普通函数作为方法
  sayHi: function() {
    // 1. 普通函数作为回调:this 指向全局(调用者是定时器,非 person)
    setTimeout(function() {
      console.log("普通函数回调:", this.name); // undefined(this 是 window)
    }, 100);

    // 2. 箭头函数作为回调:this 继承自 sayHi 的 this(即 person)
    setTimeout(() => {
      console.log("箭头函数回调:", this.name); // "Bob"(this 是 person)
    }, 100);
  }
};

person.sayHi(); 
  • 普通函数的回调:调用者是定时器(浏览器中是 window),所以 this 指向 window
  • 箭头函数的回调:定义在 sayHi 内部,父级 sayHithisperson,所以箭头函数的 this 也是 person

总结:核心区别一句话

  • 箭头函数this 是 "定义时抄父级的",一旦确定就不变(静态绑定)。
  • 普通函数this 是 "调用时看是谁调的",调用方式变了,this 就可能变(动态绑定)。

记住这个区别,就能避开大多数 this 指向的坑。

相关推荐
她说人狗殊途10 分钟前
java.net.InetAddress
java·开发语言
天使day14 分钟前
Cursor的使用
java·开发语言·ai
Dxy123931021640 分钟前
Python ExcelWriter详解:从基础到高级的完整指南
开发语言·python
chao_78944 分钟前
frame 与新窗口切换操作【selenium 】
前端·javascript·css·selenium·测试工具·自动化·html
天蓝色的鱼鱼1 小时前
从零实现浏览器摄像头控制与视频录制:基于原生 JavaScript 的完整指南
前端·javascript
阳火锅2 小时前
Vue 开发者的外挂工具:配置一个 JSON,自动造出一整套页面!
javascript·vue.js·面试
每天吃饭的羊2 小时前
react中为啥使用剪头函数
前端·javascript·react.js
源代码•宸2 小时前
C++高频知识点(十三)
开发语言·c++·经验分享·面经
wa的一声哭了3 小时前
python基础知识pip配置pip.conf文件
java·服务器·开发语言·python·pip·risc-v·os
多啦C梦a3 小时前
【适合小白篇】什么是 SPA?前端路由到底在路由个啥?我来给你聊透!
前端·javascript·架构