【JavaScript基础笔记】模块化、立即执行函数应用、闭包应用
模块化:为了提高JS代码的长期可读性,易维护性,将JS代码根据其对应的功能块区分成不同的JS文件,并将其包裹在立即执行函数内,从而使其中的变量设定为局部变量,不会造成不同模块的变量污染。
立即执行函数
在 Javascript 中,圆括号()是一种运算符,跟在函数名之后,表示调用该函数。比如,print()就表示调用print函数。
有时,我们需要在定义函数之后,立即调用该函数。这时,你不能在函数的定义之后加上圆括号,这会产生语法错误。
产生这个错误的原因是,function这个关键字即可以当作语句,也可以当作表达式。
为了避免解析上的歧义,JavaScript 引擎规定,如果function关键字出现在行首,一律解释成语句。因此,JavaScript引擎看到行首是function关键字之后,认为这一段都是函数的定义,不应该以圆括号结尾,所以就报错了。
解决方法就是不要让function出现在行首,让引擎将其理解成一个表达式。最简单的处理,就是将其放在一个圆括号里面。
上面两种写法都是以圆括号开头,引擎就会认为后面跟的是一个表示式,而不是函数定义语句,所以就避免了错误。这就叫做“立即调用的函数表达式”(Immediately-Invoked Function Expression),简称 IIFE。
注意,上面两种写法最后的分号都是必须的。如果省略分号,遇到连着两个 IIFE,可能就会报错。
推而广之,任何让解释器以表达式来处理函数定义的方法,都能产生同样的效果,比如下面三种写法。
甚至像下面这样写,也是可以的。
通常情况下,只对匿名函数使用这种“立即执行的函数表达式”。它的目的有两个:一是不必为函数命名,避免了污染全局变量;二是 IIFE 内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。
如何使用立即执行函数
ES 5 里面,只有函数有局部变量,于是我们声明一个 function xxx,然后 xxx.call() 这个时候 xxx 是全局变量(全局函数)。
所以我们不能给这个函数名字 function(){}.call() 但是 Chrome 报错,语法错误。
经过尝试得到几种方法可以不报错:
- !function(){}.call() 【我们不在乎这个匿名函数的返回值,所以加个 ! 取反没关系】
- (function(){}).call() xxx (function(){}).call() 【如果(fn).call前误写xxx,会导致意外函数执行,不推荐】
- frank192837192463981273912873098127912378.call() 【冗长繁杂,不推荐】
闭包的应用
当我们通过模块化我们的JS代码文件后,立即执行函数使得我们的模块文件无法被外部访问。
那么,如果遇到A模块需要引用B模块的变量时,我们可以通过将xxx变量添加到window的属性下,然后在另一个模块中也同样将window.xxx的地址赋值给xxx,这样就可实现模块数据沟通。
如果需要隐藏数据的细节,实现访问控制,那么我们还可以通过闭包,使得B模块可以操作由A模块里的变量构造的函数,但无法访问A模块里的变量数据
!function(){ var dinner = { main: noodle; guests : 2 } window.guestsIncrease = function(){ numeber += 1 return number } }.call()
当然我们也可以不使用window全局变量,通过自己设置的局部变量实现。本质上都是一样的将这个匿名函数的引用通过闭包保存起来,然后立即执行。
var accessor = function(){ var dinner = { main: noodle; guests : 2 } return function(){ numeber += 1 return number } } // B模块 var increase = accessor.call() increase.call()