尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

立即执行函数(IIFE)完全解析:语法格式、典型作用与闭包实战

立即执行函数(IIFE)完全解析:语法格式、典型作用与闭包实战 文档教程前端【免费下载链接】Web千古前端图文教程超详细的前端入门到进阶知识库。从零开始学前端做一名精致优雅的前端工程师。项目地址https://gitcode.com/gh_mirrors/we/Web点击查看免费下载导读立即执行函数IIFE是 JavaScript 中定义完就立刻调用的特殊函数形态广泛用于变量赋值、作用域隔离与循环闭包问题等前端实战场景。本篇以《千古前端图文教程》立即执行函数为主线结合仓库中函数简介、闭包、this 指向等章节系统梳理 IIFE 的两种语法格式、传参写法、执行时机以及为变量赋值和将全局变量变为局部变量两大核心作用帮助你把 IIFE 用对、用透。一、概念什么是立即执行函数IIFE函数定义完就立即被调用这种函数叫做立即执行函数。它的英文全称是IIFEImmediately-invoked function expression即立即调用函数表达式。从英文名称可以看出IIFE 的本质是一个函数表达式function expression而不是函数声明function declaration——这一点直接决定了它的写法为什么特殊详见下一节。在仓库的 函数简介 一章中作者把立即执行函数列为 JavaScript 中调用函数的三种主要方式之一方式1直接调用如fun();方式2通过对象的方法来调用如obj.fn2();方式3立即执行函数定义后自动调用代码举例(function() { console.log(我是立即执行函数); })();也就是说IIFE 不只是一种语法它在 JavaScript 的调用体系中是与普通函数调用对象方法调用并列的第三种常规调用手段理解这一点有助于我们在后面的章节中领会它自调用的特性。二、语法格式两种写法与传参语法1基础写法(function() { // 函数体 })();语法2立即执行函数也可以传参(function(a, b) { // 函数体 })(a, b);语法2 与语法1 的唯一区别就是在末尾的圆括号中传入实参第二个圆括号负责调用括号内的内容就是要传给 IIFE 形参的实参列表。为什么function(){}外面必须再套一对圆括号这是初学 IIFE 时最容易困惑的地方。原因在于 JavaScript 引擎的语法解析规则以function关键字开头且后面紧跟标识符函数名的语句会被解析为函数声明而function() {}是一个匿名函数它缺少函数名不能作为函数声明单独存在单独写出function() {}会直接抛出语法错误SyntaxError当我们用一对圆括号把它包起来即(function() {})JavaScript 引擎就会把括号内的内容当作函数表达式来解析。函数表达式不需要函数名因此语法合法。此时整体结构(function(){...})()的语义是先用括号把匿名函数表达式包起来再在后面紧跟一个调用括号()实现定义完立即调用。补充说明把圆括号放在函数体后面的写法(function(){...}())即包围调用括号的写法同样合法且被广泛使用两种写法效果一致。原文档采用(function(){...})()这种先表达式、后调用的形式本文统一沿用该形式。传参举例现有匿名函数如下function(a, b) { console.log(a a); console.log(b b); };上面的代码单独运行时是无法执行的——因为匿名函数必须依附于表达式上下文。改写为立即执行函数后(function(a, b) { console.log(a a); console.log(b b); })(123, 456);运行结果会依次打印a 123 b 456可以看到123和456作为实参被传入了 IIFE 的形参a、b。IIFE 在传参上与普通函数完全一致形参在函数内部成为局部变量这一特性将在后文作用域隔离中发挥关键作用。三、为什么 IIFE 往往只会执行一次原文档明确指出立即执行函数往往只会执行一次。原因在于——没有变量保存它执行完了之后就找不到它了。展开来说普通函数function foo() {...}在定义时会把函数对象赋值给变量foo之后我们随时可以多次调用foo()IIFE 则不同它是以表达式形式出现、随即被调用的匿名函数没有任何变量引用这个函数对象。一次调用结束后这个函数对象没有任何引用指向它也就无法再次调用并且很快会被垃圾回收机制回收内存。这既是 IIFE 的一次性特性也恰恰是它的价值所在用完即弃不污染命名空间。在下一节中我们会看到这个特性如何帮助我们把全局变量收编为局部变量。四、IIFE 的作用之一为变量赋值当给变量赋值需要一些较复杂的计算时使用 IIFE 会让语法显得更紧凑。常规写法先计算、再赋值let nickName; if (sex male) { nickName 帅哥; } else { nickName 美女; }IIFE 写法计算与赋值合一const sex male; const nickName (function () { if (sex male) { return 帅哥; } else { return 美女; } })(); console.log(nickName); // 打印结果帅哥两种写法结果相同但 IIFE 版本具备明显优势语法紧凑复杂的条件分支被收进函数体内外部只需一行const nickName (function(){...})();即可完成计算 赋值可直接使用const赋值语句是一次性完成的变量在声明时就被确定无需先let再赋值更符合能不改就不改的常量语义临时变量不泄漏计算过程中产生的中间变量如果有的话都局限在 IIFE 内部不会污染外层作用域。五、IIFE 的作用之二将全局变量变为局部变量循环闭包经典问题这是 IIFE 在前端面试与实战中出现频率最高的场景。问题代码函数共享同一个全局变量var arr []; for (var i 0; i 5; i) { arr.push(function () { console.log(i); }); } arr[2](); // 打印5上方代码中i是全局变量所有函数共享内存中的同一个变量i。循环结束后i的值是5因此无论调用arr[0]()、arr[2]()还是arr[4]()打印出的都是5——这与直觉期望分别打印0、1、2、3、4相悖。为什么会出现这种情况根源在于var声明的变量不产生块级作用域for循环的花括号不会为每次迭代生成独立的i同时函数内部引用的是外部变量i当函数真正执行时i早已是循环结束后的最终值。这与仓库 作用域、变量提升、函数提升 中讲解的作用域链 变量共享机制直接相关。改造代码用 IIFE 为每次迭代锁定独立的 ivar arr []; for (var i 0; i 5; i) { (function (i) { arr.push(function () { console.log(i); }); })(i); } arr[2](); // 打印2改造后arr[2]()正确打印出2。原理拆解如下i变成了 IIFE 的局部变量每一次循环迭代都会执行一次 IIFE当前迭代的i如0、1、2、3、4作为实参传入成为 IIFE 内部的形参i即 IIFE 的局部变量每个函数各持有独立的i由于 IIFE 每轮迭代都执行一次每次执行都创建了一套全新的局部变量i因此 5 个被 push 进去的函数各自绑定的是自己那一轮迭代的i互不干扰形成了闭包arr[2]()能够打印出 IIFE 内部变量i的值2说明被 push 的函数在 IIFE 执行完毕后仍然记住了 IIFE 内部的作用域这正是闭包的特征。关于闭包的机制按照仓库 闭包 的定义如果外部作用域有权访问另外一个函数内部的局部变量时那就产生了闭包。这里arr中的函数就是内部函数它引用了 IIFE外部函数的局部变量i由于闭包的存在IIFE 执行完毕后其局部变量i并不会立即销毁而是保留在内存中供后续调用。只有当所有引用它的函数都被调用完毕或引用被释放后它才会被回收——这正是立即执行函数 闭包能够锁定循环变量的本质原因。延伸ES6 时代是否有更简单的解法如果项目已支持 ES6使用let声明循环变量同样可以解决上述问题因为let具有块级作用域每次循环迭代都会生成独立的绑定。不过IIFE 的方案在旧代码库、以及必须使用var的历史代码中仍然是最通用的解法理解 IIFE 的写法依然是看懂大量存量代码的必备能力。相关知识点可进一步阅读仓库的 ES6变量 let、const 和块级作用域。六、结合源码章节的纵深延伸IIFE 与 this、作用域的关系IIFE 不仅用于赋值与循环它还与 JavaScript 的两大基础机制——this指向和全局作用域——密切相关。以下两点在仓库的其他章节中均有直接佐证。6.1 IIFE 中的 this 指向 window在仓库 this 指向 一章中明确写道以函数的形式包括普通函数、定时器函数、立即执行函数调用时this 的指向永远都是 window。比如fun();相当于window.fun();也就是说IIFE 属于以函数形式调用的范畴在非严格模式下IIFE 内部的this指向全局对象window。如果你在 IIFE 内书写this实际上访问的就是全局对象这在编写插件或模块代码时需要特别注意避免无意间向全局对象挂载属性。6.2 IIFE 与全局变量 window 属性的对应关系仓库 预编译 一章总结了两个重要规律任何变量如果未经声明就赋值此变量属于window的属性一切声明的全局变量全是window的属性即window代表了全局作用域。由此反推 IIFE 的价值就非常清晰了凡是写在全局作用域的var声明都会成为window的属性从而造成全局污染例如团队协作中变量名互相覆盖。而 IIFE 自带局部作用域把代码包进 IIFE 之后内部声明的变量就进不了window也就不会污染全局命名空间——这正是 IIFE将全局变量变为局部变量这一作用最底层的机制支撑。6.3 IIFE 与模块封装进一步地IIFE 经常被用来实现模块模式把数据和功能都封装在函数内部只向外暴露指定的对象或方法。仓库 闭包 一章中提到闭包的一个重要使用场景就是定义具有特定功能的 JS 模块将所有的数据和功能都封装在一个函数内部只向外暴露指定的对象或方法。IIFE 正是这种模块模式最常见的载体比如var myModule (function () { var privateCount 0; // 私有变量外部无法直接访问 return { increment: function () { privateCount; }, getCount: function () { return privateCount; } }; })(); myModule.increment(); myModule.increment(); console.log(myModule.getCount()); // 打印2 // console.log(myModule.privateCount); // undefined私有变量对外不可见在这个例子中IIFE 一次性执行并返回一个对象privateCount成为模块内部的私有变量外部只能通过暴露的increment、getCount方法间接操作它——既实现了信息隐藏又避免了全局变量污染。七、总结IIFE 的三个关键记忆点形态(function(){...})()是 IIFE 的标准形态核心是函数表达式 立即调用function(){}单独写是非法的必须用圆括号包裹使其成为表达式。特性IIFE 定义完立即执行且通常只执行一次——因为没有任何变量保存它执行后就找不到了形参在 IIFE 内部是局部变量这是作用域隔离的基础。两大核心作用为变量赋值把复杂的计算过程封装进 IIFE语法紧凑、支持const一次性赋值、中间变量不泄漏将全局变量变为局部变量最典型的场景是循环闭包问题——通过 IIFE 为每次迭代创建独立的局部变量i让arr[2]()正确打印2而非5同时天然形成闭包。掌握 IIFE本质上就掌握了 JavaScript 作用域、函数表达式与闭包三个核心概念的交叉点。它既是一个高频面试考点也是阅读和理解大量前端源码jQuery 插件、模块化代码、防抖节流工具函数等的必备基础。如果你希望继续深入可以接着阅读仓库的 闭包、this 指向 与 作用域、变量提升、函数提升 三篇文章形成完整的知识闭环。赞分享文档教程前端【免费下载链接】Web千古前端图文教程超详细的前端入门到进阶知识库。从零开始学前端做一名精致优雅的前端工程师。项目地址https://gitcode.com/gh_mirrors/we/Web点击查看免费下载相关推荐ESLint wrap-iife 规则详解用括号规范 IIFE 立即调用函数的书写风格ESLint wrap iife 规则详解用括号规范 IIFE 立即调用函数的书写风格 本篇技术指南以 ESLint 内置规则 wrap iife 为核心讲开发工具Lint静态分析代码质量JavaScript立即执行函数(IIFE)从入门到精通的终极指南JavaScript立即执行函数 IIFE 从入门到精通的终极指南 JavaScript立即执行函数IIFE是前端开发中至关重要的概念它能帮助开发者解决教程《仓颉语言实战》仓颉Lambda表达式、闭包与管道语法糖函数高级用法完全指南《仓颉语言实战》仓颉Lambda表达式、闭包与管道语法糖函数高级用法完全指南 《仓颉语言实战》是张磊编著、清华大学出版社出版的仓颉语言入门教程本书示例代码库示例工程上一篇ControlNet-v1-1_fp16_safetensors学习路径规划从新手到专家的成长路线下一篇推荐一款高效能的Delaunay三角剖分库Delaunator创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表