当即调用的函数表达式是咱们常用的函数编码模式之一,也被称之为 IIFE 。javascript
在咱们了解 IIFE 是什么以及为何须要它以前,咱们须要快速回顾一下关于 Javascript 函数中的基本概念。java
function say() {
console.log('Hello World');
}
say(); // print: Hello World
复制代码
这种建立函数的方式称之为:函数声明。git
一般,刚接触 Javascript 的开发人员使用这种语法没有问题, 由于它很是相似于其余流行的编程语言中的函数或方法。github
咱们须要注意的是,函数声明老是以 function 关键字开头,后面紧跟着函数的名称, 而且名称不可省略。express
let msg = 'Hello World';
let say = function() {
console.log(msg);
}
say(); // print:Hello World
复制代码
在上面的例子中,咱们为变量 say 赋予了函数类型的值, 这种赋值运算符右侧的函数一般称为: 函数表达式。编程
函数表达式在 Javascript 中无处不在,咱们可能编写的大多数回调函数一般都是函数表达式。浏览器
所以,咱们要知道一个重要的概念,在 Javascript 中函数几乎与其余任何值同样, 既能够位于赋值运算符的右侧,也能够做为参数传递给其余函数。安全
顾名思义,匿名函数就是没有名字的函数。闭包
let say = function() {
console.log('Hello World');
}
复制代码
咱们建立了一个函数表达式,而它其实也是匿名函数, 由于 function 关键字后面没有名字。编程语言
函数表达式一样能够有函数名称,此时函数名称只是函数体内的一个本地变量,没法在外部调用, 它的最主要的做用是在递归中使用。
let fn = function say() {
console.log('Hello World');
}
say(); // print error :say is not defined
复制代码
咱们已经简单回顾了函数定义和函数表达式,如今让咱们直接进入 IIFE 的秘密世界。
咱们但愿获得的是一个能够当即执行的函数,那么咱们声明一个函数, 而后经过括号()来执行不就好了么,咱们来试一下
它们有几种风格。让咱们首先看到一个很是容易理解的变体。
!function() {
console.log("Hello from IIFE!");
}();
复制代码
当帮这段代码复制到浏览器的控制台中尝试时,会在控制台中打印咱们想要输出的内容。
接下来,咱们来理解这段不是那么直观的代码:
在 Javascript 语言中,当 function 关键字视为有效语句中的第一个单词时, Javascript 引擎会将其当作函数声明来处理。 所以,为了防止这种状况的发生,咱们在第 1 行代码中的 function 关键字前加上 “!”前缀。 此时 Javascript 会将这段代码视为函数表达式。
在代码的第 3 行,经过一对圆括号对函数表达式进行调用。
因此,咱们得出结论,什么是 IIFE:函数表达式,在建立后当即执行,就成为 IIFE。
经过将“!”替换为“+”,“-”,“~”甚至“void”,“typeof”甚至“delete”。
咱们能够在浏览器的控制台上,尽情的尝试~
!function(){console.log('!')}();
+function(){console.log('+')}();
-function(){console.log('-')}();
~function(){console.log('~')}();
void function(){console.log('void')}();
typeof function(){console.log('typeof')}();
delete function(){console.log('delete')}();
复制代码
上面例子中,关键字 function 前的“!、+、-、~、void、typeof、delete”所作的事, 就是将函数转换为函数表达式而不是函数声明。
这种方式构造的 IIFE 很容易理解,接下来咱们介绍其余更传统和更普遍使用的 IIFE 风格。
(function(){
console.log("I'm not IIFE")
})
复制代码
在上面的代码中,函数表达式包含在第 1-3 行的括号中。 它还不是 IIFE,由于函数表达式永远不会被执行。 如今要将该代码转换为 IIFE,咱们有如下两种风格变化:
//方法1
(function(){
console.log("I'm IIFE")
})()
//方法2
(function(){
console.log("I'm IIFE")
}())
复制代码
如今咱们生成了 2 个 IIFE,可能很难注意到这两者的区别,解释一下:
这两种方法都被普遍的使用,咱们能够根据本身的喜爱使用其中任何一个。
如今让咱们再看一个有效的例子,以及两个无效的例子。 咱们将从如今开始命名咱们的 IIFE,由于使用匿名函数一般不是一个好主意。
//有效的IIFE
(function initAppIIFE() {
//全部你的神奇代码
}());
//如下两个是无效的IIFE示例
function nonWorkingIIFE() {
//如今你知道为何你须要我周围的那些括号!
//若是没有这些括号,我是一个函数声明,而不是一个表达式。
//你会收到语法错误!
}();
function () {
//这里也会出现语法错误!
}();
复制代码
咱们得出一个结论:
只有函数表达式可以建立 IIFE ,函数声明永远不会用于建立 IIFE。
IIFE 很是擅长的一件事就是可以为 IIFE 建立私有做用域。
在 IIFE 内声明的任何变量对外界都不可见。
来看一个例子:
(function IIFE_initApp() {
// IIFE 的私有变量,外部没法访问
let lives;
let weapons;
init();
// IIFE 的私有函数,外部没法访问
function init() {
lives = 5;
weapons = 10;
}
}());
复制代码
当咱们须要建立一堆变量和函数时,咱们定义一个 IIFE 并在 IIFE 内建立咱们所需的变量和函数, 这样既不会污染全局空间,也能够保护本身的代码不被他人之外的影响。
若是咱们不须要来自 IIFE 的返回值,那么咱们可使用咱们最早介绍的经过一元运算符(!、+、-、~等)来实现的IIFE。
~function IIFE_initApp() {
//...
}
复制代码
但 IIFE 的另外一个很是重要且强大的功能是它们能够返回能够分配给变量的值。
let result = (function() {
return "From IIFE";
}());
console.log(result); // print:"From IIFE"
复制代码
在上面的代码中,咱们建立了一个 IIFE 当即执行,并将返回值传递给变量 result
这是一个很是强大的功能,咱们将在后文介绍模块的时候来使用它。
IIFE 不只能够有返回值,也能够在调用时进行参数传递,咱们来看一下:
(function IIFE(msg, times) {
for (let i = 1; i <= times; i++) {
console.log(msg);
}
}("Hello!", 5));
复制代码
在这个例子中,咱们建立了一个 IIFE 而且给予了两个参数 msg 和 times。
这是一个很是强大的功能,咱们常常在 Jquery 或其余库中看到这种使用方式。
(function($, global, document) {
// use $ for jQuery, global for window
}(jQuery, window, document));
复制代码
在这个例子中,咱们将 jQuery、window 和 document 做为参数传递给 IIFE, 在 IIFE中的代码使用 $、global 和 document 做为形参变量来接收这三个实参。
这种传递参数的有点以下:
下面咱们经过 IIFE 来实现一个模块模式的例子。
咱们来实现一个经典的 Sequence 对象,咱们分为两步编写此代码,以便逐步了解正在发生的事情。
let Sequence = (function sequenceIIFE() {
// IIFE 中的私有变量
let current = 0;
// 经过 IIFE 返回一个空对象
return {};
}());
console.log(typeof Sequence); // print:"object"
复制代码
在上面的例子中,咱们建立了一个返回空对象的 IIFE,而且在 IIFE 内建立了一个私有变量 current。
接下来,咱们作一些改进,在 IIFE 返回的空对象中添加一些函数。
let Sequence = (function sequenceIIFE() {
// IIFE 中的私有变量
let current = 0;
// 经过 IIFE 返回一个空对象
return {
getCurrentValue: function() {
return current;
},
getNextValue: function() {
current = current + 1;
return current;
}
};
}());
console.log(Sequence.getNextValue()); // 1
console.log(Sequence.getNextValue()); // 2
console.log(Sequence.getCurrentValue()); // 2
复制代码
在这个例子中,咱们在 IIFE 返回的对象中,添加了两个函数 getNextValue 和 getCurrentValue。
getCurrentValue 用于将当前的 current 的值返回。
getNextValue 用于将 current 的值递增 1 ,而且返回当前 current 的值。
因为 IIFE 中的变量 current 是私有的,外部没法访问的,由于只有经过 getCurrentValue 或 getNextValue 函数才能访问它的值。
这是一个很是强大的 Javascript 模块模式,它结合了 IIFE 和闭包的强大功能。
用于包裹函数体的括号是用于强制将咱们正在操做的函数体变为函数表达式。
可是当 Javascript 引擎能够确认这是一个函数表达式时,咱们就不须要包裹在函数外的括号了。
let result = function() {
return 'From IIFE'
}();
console.log(result);
复制代码
在上面的示例中,function 关键字不是语句中的第一个单词。所以 Javascript 引擎不会将其视为函数声明。
即便如此,依然仍是建议在函数体外包裹一对括号。
使用括号能够经过在第一行上对读者进行风格上的暗示,暗示该函数将成为 IIFE , 而没必要要滚动到函数的最后一行才能肯定是不是 IIFE ,所以使用括号能够提升代码的可读性。
预计每周1-2篇文章,持续更新,欢迎各位同窗点赞+关注
后续内容参见写做计划
写做不易,若是以为稍有收获,欢迎~点赞~关注~