
JavaScript 函数本质上是“一等公民”(first-class object),这意味着它和其他数据类型(如数字、字符串)地位相同,可以被赋值给变量、存入数组、作为对象属性,也能当作参数传给其他函数,甚至能作为返回值。这不是语法糖,而是语言设计的核心特性。
函数是值,不是指令块
很多人初学时把函数理解为“一段要执行的代码”,但 JS 中更准确的理解是:函数是一个可调用的对象,本身就是一个值。就像 let x = 42 中的 42 是数值字面量,function() {} 或 () => {} 是函数字面量,生成的是一个函数对象。
正因为它是值,所以可以:
- 赋给变量:
const greet = function(name) { return 'Hello ' + name; }; - 放进数组:
const tasks = [greet, console.log, Math.max]; - 作为参数传入:
[1, 2, 3].map(x => x * 2)中的箭头函数就是传入的参数 - 作为返回值:
function makeAdder(n) { return function(x) { return x + n; }; }
回调函数是最典型的体现
当函数被当作参数传递,通常是为了“延迟执行”或“交由他人调度”。最常见场景就是事件处理和数组方法:
立即学习“Java免费学习笔记(深入)”;
-
button.addEventListener('click', handleClick):把handleClick函数交给浏览器,等用户点击时再调用 -
arr.filter(item => item > 10):把判断逻辑封装成函数,交给filter内部循环调用 -
setTimeout(() => console.log('done'), 1000):把要执行的逻辑推迟交给定时器系统
这些例子中,接收方(如 addEventListener、filter、setTimeout)并不关心你传的是什么函数,只负责在合适时机用 () 调用它——这正是“函数可被调用”这一能力的直接体现。
底层支持:函数对象自带 [[Call]] 内部方法
ECMAScript 规范规定,函数对象内部有一个不可见的 [[Call]] 方法。当你写 myFn(),JS 引擎实际是在触发这个内部方法。而所有具备 [[Call]] 的对象,都可被调用,也自然可以被当作参数传递——因为参数传递只是把值(包括函数对象)拷贝过去,不涉及立即执行。
对比一下普通对象:const obj = {} 没有 [[Call]],所以 obj() 会报错 TypeError: obj is not a function;而函数对象天生拥有该能力,因此安全可传、可存、可调。
为什么基本类型不能这样?
数字、字符串等原始值没有 [[Call]],也不能加括号调用,所以无法充当“可执行逻辑”的载体。而函数填补了这个空白:它既是数据(可传递、可存储),又是行为(可执行、可复用)。这种统一性,正是 JS 灵活性和函数式编程风格的基础。











