导读:本期聚焦于小团团创作的《JavaScript中的函数声明、函数表达式与箭头函数有何本质区别?》,敬请观看详情。同样一段赋值逻辑,为什么有些函数能在定义前调用,有些却会抛出引用错误?这个问题背后是JavaScript引擎对函数声明、函数表达式和箭头函数完全不同的处理机制。函数声明存在变量提升,会在作用域创建阶段整体挂载;函数表达式只提升变量名,赋值要等到代码执行;箭头函数除了没有自己的this和arguments,也不能作为构造函数,更不会自动绑定参数对象。三者看似都能定义可执行逻辑,但在提升行为、this指向、原型属性、new调用和适用场景上差异巨大。理解这些边界,能帮助开发者避免在事件回调、类方法、动态创建函数时踩坑。本文从解析阶段和执行阶段入手,结合可运行示例逐一拆解它们的本质区别。

在JavaScript中定义函数至少存在三种主流方式:使用function关键字开头的函数声明、把匿名函数赋值给变量的函数表达式,以及使用箭头符号的箭头函数。它们都能封装一段可执行逻辑,但解析时序、this绑定规则、原型对象以及可用的语言特性并不相同。如果只停留在语法层面理解,很难解释为什么有些函数可以提前调用,而另一些则会在运行时抛出错误。

JavaScript中的函数声明、函数表达式与箭头函数有何本质区别?

提升行为:执行前发生了什么

JavaScript在执行任何代码之前会先进入作用域创建阶段。这个阶段会扫描当前作用域内的变量和函数声明,并把它们登记到执行上下文中。函数声明会被整体提升,也就是说函数名和函数体都会在代码执行前被挂载到当前作用域。因此,在函数声明语句之前调用该函数是可以正常工作的。

// 函数声明:可以在定义前调用
console.log(add(2, 3)); // 5
function add(a, b) {
  return a + b;
}

函数表达式则不同。无论使用var、let还是const声明变量,赋值操作都发生在代码执行阶段。使用var时,变量名会先被提升为undefined,但函数体还没有赋值;如果此时调用,会得到一个类型错误。使用let或const时,变量会进入暂时性死区,访问时会直接抛出引用错误。箭头函数本质上是函数表达式的一种简写形式,因此同样不会整体提升。

// 函数表达式:调用时机错误
console.log(subtract(5, 2)); // TypeError: subtract is not a function
var subtract = function (a, b) {
  return a - b;
};

// 箭头函数:同样不会整体提升
console.log(multiply(2, 4)); // ReferenceError: Cannot access 'multiply' before initialization
const multiply = (a, b) => a * b;

块级作用域中的函数声明还要更复杂一些。在ES6之前,函数声明只能出现在全局作用域或函数作用域中;ES6之后允许在块级作用域内声明函数,但不同的JavaScript引擎对块级函数的提升策略存在差异。为了避免这种不确定性,在需要动态定义函数或者条件定义函数时,更推荐使用函数表达式或箭头函数,让代码的执行顺序更加直观。

this与arguments:词法作用域与动态绑定的分界

普通函数内部的this值并不是在定义时确定的,而是取决于调用方式。作为对象方法调用时,this指向该对象;使用call、apply或bind调用时,this指向显式指定的对象;作为构造函数使用时,this指向新创建的实例。函数声明和函数表达式都遵循这一套动态绑定规则。

const obj = {
  name: 'ipipp',
  regular: function () {
    return this.name;
  },
  arrow: () => {
    return this.name;
  }
};
console.log(obj.regular()); // ipipp
console.log(obj.arrow()); // undefined,外层this不是obj

箭头函数没有自己的this。它不会根据调用方式重新绑定this,而是直接继承定义箭头函数时所在外层作用域的this。这种特性让箭头函数非常适合用在回调函数中,尤其是需要访问外层实例对象的场景。例如在setTimeout回调里使用箭头函数,可以继续使用外层函数的this,而不需要提前保存self或that变量。

const handler = {
  count: 0,
  start: function () {
    setTimeout(() => {
      this.count++;
      console.log(this.count);
    }, 100);
  }
};
handler.start(); // 1

arguments对象也存在明显差异。普通函数内部可以访问arguments对象,它包含了调用时传入的所有参数,无论形参是否定义。箭头函数没有自己的arguments对象,如果在箭头函数内部访问arguments,得到的其实是外层作用域中的arguments。现代开发中更推荐使用剩余参数来替代arguments,因为剩余参数是真正的数组,可以直接使用数组方法。

构造函数、原型与适用场景

普通函数都可以作为构造函数与new关键字一起使用。执行new操作时,JavaScript会创建一个新对象,把该对象链接到构造函数的prototype属性,并把this绑定到新对象上,最后返回这个对象。函数声明和函数表达式都有prototype属性,这意味着它们可以被用来创建对象实例。

function Person(name) {
  this.name = name;
}
console.log(typeof Person.prototype); // object

const Dog = (name) => {
  this.name = name;
};
// new Dog('x'); // TypeError: Dog is not a constructor
console.log(Dog.prototype); // undefined

箭头函数不能用作构造函数。它没有prototype属性,也不能通过new实例化。如果强行使用new调用箭头函数,JavaScript会抛出类型错误。这个限制并不是缺陷,而是箭头函数设计目标的自然结果:它更关注简洁的回调逻辑,而不是创建可继承的对象类型。需要定义类、创建实例、设置原型方法时,应该使用普通函数或ES6的class语法。

在实际开发中,事件监听回调、数组的map、filter、reduce方法、Promise的then链以及定时器回调通常优先使用箭头函数,因为这些场景需要保持外层this不变。而在需要动态this、使用arguments对象、定义构造函数或者需要给函数显式绑定this时,普通函数声明或函数表达式仍然是更合适的选择。

理解三者的本质区别,核心在于区分解析阶段和执行阶段、动态this和词法this、构造能力和表达式赋值这三组概念。函数声明的优势是提升和可读性,函数表达式的优势是灵活和可控,箭头函数的优势是简洁和稳定的this绑定。根据具体场景选择正确的函数形式,才能写出更可靠、更易维护的JavaScript代码。

JavaScript函数声明函数表达式箭头函数修改时间:2026-08-20 19:17:18

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。