函数声明可提升,定义前可调用;函数表达式不可提升,须先赋值再调用;箭头函数属表达式且无this,继承外层this;块级作用域仅限let/const,function声明仍提升至函数作用域;IIFE在ES6后需求减少但仍有私有上下文等不可替代场景。
JavaScript 里定义函

function 声明和函数表达式,它们的行为差异直接影响调用时机和作用域可见性。
函数声明会被提升(hoisted),意味着你可以在定义前就调用它;函数表达式不会,必须先赋值再调用,否则会报 TypeError: xxx is not a function。
function sayHello() { return "hi"; } —— 可以在声明前调用const sayHi = function() { return "hi"; }; —— 必须在赋值后调用,否则 sayHi 是 undefined
const sayBye = () => "bye"; 属于函数表达式,同样不提升函数调用方式直接决定 this 的值,不是看函数怎么定义的,而是看怎么被调用的。
foo() → this 在非严格模式下是 window(浏览器)或 globalThis,严格模式下是 undefined
obj.method() → this 是 obj
call/apply/bind 显式绑定 → this 按传入对象确定this,它沿用外层函数作用域的 this,这点常被误以为“更安全”,但其实只是继承,不能重绑定函数作用域是 JavaScript 最早的作用域模型,而 let/const 引入的块级作用域只对它们自身有效,不影响函数声明和 var 变量。
if 或 for 块里用 function foo(){} 声明,该函数仍会提升到整个函数作用域顶部(不是块内)let foo = function(){} 才真正受块限制,块外访问会报 ReferenceError
let 变量ES6 之后,let/const 和模块系统让 IIFE 的“隔离变量”需求大幅下降,但它在特定场景仍有不可替代性。
(function(){ let privateVar = 42; window.myAPI = { get: () => privateVar }; })();
var + setTimeout 更直观(不过现在更推荐用 let 声明循环变量)function 声明的函数仍受函数作用域约束,不是块作用域作用域链的实际表现往往藏在调用栈深处,尤其当高阶函数、异步回调和 this 绑定混用时,最易出问题的是「以为函数记住了某个值,其实它引用的是变量本身」——这恰恰是闭包的本意,也是最容易被忽略的细节。