JavaScript函数是一等公民,可赋值、传参、返回;function声明全提升,表达式仅变量名提升;箭头函数无this/arguments;参数不校验个数,支持默认值和剩余参数;IIFE用于作用域隔离,call/apply/bind控制this绑定。
它不是对象、类或语法糖,而是一等公民(first-class citizen):可以赋值给变量、作为参数传入其他函数、从函数中返回。定义后不自动运行,必须显式调用。
区别不在功能,而在**提升(hoisting)行为**和**命名可见性**上,直接影响调用时机和调试体验:
function 声明会被完全提升,可在定义前调用;const myFn = function() {} 这类表达式只提升变量名(myFn),不提升函数体,提前调用会报 TypeError: myFn is not a function
anonymous;加上名字如 const myFn = function namedFn() {},既利于调试,又能让函数内部递归调用自己(namedFn())const myFn = () => {} 不能用作构造函数,且没有自己的 this、arguments、new.target
JS 不检查实参个数是否匹配形参,多传或少传都不会报错,但语义可能出问题:
undefined;可用默认参数规避:function greet(name = 'Guest') { ... }
arguments 类数组访问(仅普通函数),或用剩余参数 function(...args) { }
obj = {} 只改本地绑定)它们解决的是「执行上下文控制」和「环境隔离」问题,不是炫技:
FE(如 (function(){...})())常用于避免污染全局作用域,或创建私有变量(闭包);ES6 后更多用模块封装替代call 和 apply 立即调用并指定 this,区别只在参数传法:fn.call(obj, a, b) vs fn.apply(obj, [a, b])
bind 返回新函数,this 和部分参数被固化,适合事件回调、防抖节流等需预设上下文的场景const user = { name: 'Alice' };
function introduce(greeting, punctuation) {
return `${greeting}, I'm ${this.name}${punctuation}`;
}
introduce.call(user, 'Hi', '!'); // "Hi, I'm Alice!"
const boundIntro = introduce.bind(user, 'Hello');
boundIntro('.'); // "Hello, I'm Alice."
函数的“复杂点”往往不在定义语法,而在调用时的 this 绑定、参数传递的隐式转换、以及闭包中对外部变量的捕获时机——这些地方稍不注意,就会出现值不对、this 指向丢失、内存泄漏等问题。