Appearance
作用域与IIFE
元信息
- 目标:理解模块化诞生前夜的语言背景——js 只有全局/函数/块级作用域而无文件作用域,多脚本共页必然名字冲突;掌握用 IIFE 圈出私有空间、化解变量与函数名冲突的手法
- 关键概念:全局作用域、函数作用域、块级作用域、变量名冲突、IIFE、闭包
- 关联阶段:cp3
- 常见误区:以为每个 js 文件有独立作用域(无文件作用域,引入即共享全局);以为 IIFE 是新语法(只是"定义后立即调用"的函数表达式惯用法);以为改名就能躲开冲突(库一多指数爆炸,收进 IIFE 局部才是解)
Javascript的作用域
- 全局作用域(global scope)
- 函数作用域(function scope)
- 块级作用域(block scope)
由于早期脚本语言的设计定位,传统 js 脚本没有文件作用域(file scope)——即"每个文件独享一个作用域"并不成立:页面引入的多个 <script> 共享同一个全局作用域。文件作用域(近似于模块作用域或命名空间)要到 ESM 才出现。
问题:变量名冲突
counter变量用于保存状态,但如果放置于全局空间,即当前文件引入了另一个js文件,而该js文件中的脚本也使用了counter变量即变量名冲突,将形成冲突,怎么办?
解决方法:闭包
- 上述代码先是定义了一个匿名函数,并且被立即执行。执行后的结果是另一个匿名函数对象的引用
- 利用了js中的闭包特性,来解决变量的生命周期(全局可用)和作用域(局部可见)的问题,从而实现了变量的本地化
- 函数式编程的特性:函数可以被动态地定义,允许作为输入参数、输出参数。
问题:函数名称冲突
$()函数是个十分常用的名字,曾经有多个流行的js库都定义了$(),如Jquery、Prototype等。因此,当某页面需要同时引用这两个库或者当你的自编库也定义了$()函数。那么,如何避免冲突?
javascript
<script src="./jquery.js"></script>
<script src="./prototype.js"></script>
<script>
$('#info').text('该字段不能为空!'); // `$()`函数出现歧义
</script>解决方法:立即执行函数
立即执行函数(IIFE, Immediately Invoked Function Expression)即"定义后马上调用"的函数表达式惯用法,它借助闭包实现私有变量,用于解决函数名称冲突的问题:
- 通过声明一个匿名函数,将
$名字仅限于该匿名函数内部 - 然后立即执行该函数,在执行时,将传入的外部函数名称动态映射为内部函数名称
javascript
(function ($) {
$('#info').text('该字段不能为空!');
})(window.jQuery);IIFE帮助开发者间接实现了一个局部化的作用域,从而:
- 模块化: 外部函数和内部函数可以同名;不同的库只需要保持唯一的库名;一般来说,库名远远少于函数名且易于管理。
- 依赖注入:外部库以参数传入、映射为形参名,接口不变而来源可替换
- 性能提升:全局变量的局部化,减少对全局变量作用域的查找
参考
- https://developer.mozilla.org/zh-CN/docs/Glossary/%E7%AB%8B%E5%8D%B3%E6%89%A7%E8%A1%8C%E5%87%BD%E6%95%B0%E8%A1%A8%E8%BE%BE%E5%BC%8F
- 糟糕的javscript https://github.com/denysdovhan/wtfjs/blob/master/README-zh-cn.md
- http://benalman.com/news/2010/11/immediately-invoked-function-expression/#iife
- https://www.cnblogs.com/xiezhi/p/5653130.html
- bootstrap代码解析 http://suqing.iteye.com/blog/1984131