如果您对js立即执行函数IIFE和js立即执行函数感兴趣,那么这篇文章一定是您不可错过的。我们将详细讲解js立即执行函数IIFE的各种细节,并对js立即执行函数进行深入的分析,此外还有关于#立即执行函
如果您对js 立即执行函数 IIFE和js 立即执行函数感兴趣,那么这篇文章一定是您不可错过的。我们将详细讲解js 立即执行函数 IIFE的各种细节,并对js 立即执行函数进行深入的分析,此外还有关于# 立即执行函数(IIFE)和闭包、7种JS-IIFE(立即执行函数)写法、IIFE立即执行函数、IIFF(立即执行函数表达式)的实用技巧。
本文目录一览:js 立即执行函数 IIFE(js 立即执行函数)
原文链接: js 立即执行函数 IIFE
上一篇: js worker 性能测试 fib
下一篇: WebAssembly 简单介绍
1. 定义
IIFE: Immediately Invoked Function Expression,意为立即调用的函数表达式,也就是说,声明函数的同时立即调用这个函数。
对比一下,这是不采用IIFE时的函数声明和函数调用:
function foo(){ var a = 10; console.log(a); } foo();
下面是IIFE形式的函数调用:
(function foo(){ var a = 10; console.log(a); })();
函数的声明和IIFE的区别在于,在函数的声明中,我们首先看到的是function关键字,而IIFE我们首先看到的是左边的(。也就是说,使用一对()将函数的声明括起来,使得JS编译器不再认为这是一个函数声明,而是一个IIFE,即需要立刻执行声明的函数。
两者达到的目的是相同的,都是声明了一个函数foo并且随后调用函数foo。
2. 为什么需要IIFE?
如果只是为了立即执行一个函数,显然IIFE所带来的好处有限。实际上,IIFE的出现是为了弥补JS在scope方面的缺陷:JS只有全局作用域(global scope)、函数作用域(function scope),从ES6开始才有块级作用域(block scope)。对比现在流行的其他面向对象的语言可以看出,JS在访问控制这方面是多么的脆弱!那么如何实现作用域的隔离呢?在JS中,只有function,只有function,只有function才能实现作用域隔离,因此如果要将一段代码中的变量、函数等的定义隔离出来,只能将这段代码封装到一个函数中。
在我们通常的理解中,将代码封装到函数中的目的是为了复用。在JS中,当然声明函数的目的在大多数情况下也是为了复用,但是JS迫于作用域控制手段的贫乏,我们也经常看到只使用一次的函数:这通常的目的是为了隔离作用域了!既然只使用一次,那么立即执行好了!既然只使用一次,函数的名字也省掉了!这就是IIFE的由来。
3. IIFE的常见形式
根据最后表示函数执行的一对()位置的不同,常见的IIFE写法有两种,示例如下:
列表1:IIFE写法一
(function foo(){ var a = 10; console.log(a); })();
列表2:IIFE写法二
(function foo(){ var a = 10; console.log(a); }());
这两种写法效果完全一样,使用哪种写法取决于你的风格,貌似第一种写法比较常见。
其实,IIFE不限于()的表现形式[1],但是还是遵守约定俗成的习惯比较好。
4. IIFE的函数名和参数
根据《You Don’t Know JS:Scope & Clouses》[2]的说法,尽量避免使用匿名函数。但是IIFE确实只执行一次,给IIFE起个名字有些画蛇添足了。如果非要给IIFE起个名字,干脆就叫IIFE好了。
IIFE可以带(多个)参数,比如下面的形式:
var a = 2; (function IIFE(global){ var a = 3; console.log(a); // 3 console.log(global.a); // 2 })(window); console.log(a); // 2
5. IIFE构造单例模式
JS的模块就是函数,最常见的模块定义如下:
function myModule(){ var someThing = "123"; var otherThing = [1,2,3]; function doSomeThing(){ console.log(someThing); } function doOtherThing(){ console.log(otherThing); } return { doSomeThing:doSomeThing, doOtherThing:doOtherThing } } var foo = myModule(); foo.doSomeThing(); foo.doOtherThing(); var foo1 = myModule(); foo1.doSomeThing();
如果需要一个单例模式的模块,那么可以利用IIFE:
var myModule = (function module(){ var someThing = "123"; var otherThing = [1,2,3]; function doSomeThing(){ console.log(someThing); } function doOtherThing(){ console.log(otherThing); } return { doSomeThing:doSomeThing, doOtherThing:doOtherThing } })(); myModule.doSomeThing(); myModule.doOtherThing();
6. 小结
IIFE的目的是为了隔离作用域,防止污染全局命名空间。
ES6以后也许有更好的访问控制手段(模块?类?),有待研究。
javascript和其他编程语言相比比较随意,所以javascript代码中充满各种奇葩的写法,有时雾里看花,当然,能理解各型各色的写法也是对javascript语言特性更进一步的深入理解。
( function(){…} )()和( function (){…} () )是两种javascript立即执行函数的常见写法,最初我以为是一个括号包裹匿名函数,再在后面加个括号调用函数,最后达到函数定义后立即执行的目的,后来发现加括号的原因并非如此。要理解立即执行函数,需要先理解一些函数的基本概念。
函数声明、函数表达式、匿名函数
函数声明:function fnName () {…};使用function关键字声明一个函数,再指定一个函数名,叫函数声明。
函数表达式 var fnName = function () {…};使用function关键字声明一个函数,但未给函数命名,最后将匿名函数赋予一个变量,叫函数表达式,这是最常见的函数表达式语法形式。
匿名函数:function () {}; 使用function关键字声明一个函数,但未给函数命名,所以叫匿名函数,匿名函数属于函数表达式,匿名函数有很多作用,赋予一个变量则创建函数,赋予一个事件则成为事件处理程序或创建闭包等等。
函数声明和函数表达式不同之处在于,一、Javascript引擎在解析javascript代码时会‘函数声明提升’(Function declaration Hoisting)当前执行环境(作用域)上的函数声明,而函数表达式必须等到Javascirtp引擎执行到它所在行时,才会从上而下一行一行地解析函数表达式,二、函数表达式后面可以加括号立即调用该函数,函数声明不可以,只能以fnName()形式调用 。以下是两者差别的两个例子。
1 2 3 4 5 6 7 8 9 10 11 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
在理解了一些函数基本概念后,回头看看( function(){…} )()和( function (){…} () )这两种立即执行函数的写法,最初我以为是一个括号包裹匿名函数,并后面加个括号立即调用函数,当时不知道为什么要加括号,后来明白,要在函数体后面加括号就能立即调用,则这个函数必须是函数表达式,不能是函数声明。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
可以看到输出结果,在function前面加!、+、 -甚至是逗号等到都可以起到函数定义后立即执行的效果,而()、!、+、-、=等运算符,都将函数声明转换成函数表达式,消除了javascript引擎识别函数表达式和函数声明的歧义,告诉javascript引擎这是一个函数表达式,不是函数声明,可以在后面加括号,并立即执行函数的代码。
加括号是最安全的做法,因为!、+、-等运算符还会和函数的返回值进行运算,有时造成不必要的麻烦。
不过这样的写法有什么用呢?
javascript中没用私有作用域的概念,如果在多人开发的项目上,你在全局或局部作用域中声明了一些变量,可能会被其他人不小心用同名的变量给覆盖掉,根据javascript函数作用域链的特性,可以使用这种技术可以模仿一个私有作用域,用匿名函数作为一个“容器”,“容器”内部可以访问外部的变量,而外部环境不能访问“容器”内部的变量,所以( function(){…} )()内部定义的变量不会和外部的变量发生冲突,俗称“匿名包裹器”或“命名空间”。
JQuery使用的就是这种方法,将JQuery代码包裹在( function (window,undefined){…jquery代码…} (window)中,在全局作用域中调用JQuery代码时,可以达到保护JQuery内部变量的作用。
# 立即执行函数(IIFE)和闭包
IIFE
IIFE: immediately-invoked function expression,立即调用函数表达式,函数在定义的时候,立即执行,是一种语法。
前置知识
了解函数声明和函数表达式的区别
- 以 function 开头的就是函数声明
- 痛过赋值给变量的就是函数表达式
例子:
function a() {} // 函数声明
var a = function () {}; // 函数表达式
形式
(function () {})(); // 形式1
(function () {}()); // 形式2
+function () {}(); // 形式3
-function () {}(); // 形式4
!function () {}(); // 形式5
~function () {}(); // 形式6
- 声明一个函数,一般是匿名函数,因为立即执行函数是不需要名字的
- 将函数声明转换成函数表达式,可以使用下面四个一元运算符(只用一个操作数):+ - ! ~(按位取反运算符),或者用小括号包裹起来
- 让表达式执行,在表达式后面添加小括号让函数立即执行
作用
- 不需要为函数命名,避免污染全局变量
- 创建一个独立作用域,这个作用域里面的变量外界访问不到,避免污染全局变量
使用场景
- 页面加载完后,需要立即执行一些初始化设置,例如事件处理,创建对象等
- 在应用中只执行一次的代码
闭包
这个知识点来来回回总结了好多次,主要自己没有一个太明确的概念,而且再工作中用的场景也很好,后面遇到再好好补充。
闭包:在函数外部访问函数内部的变量的函数,就是闭包,正常情况下在函数外部是访问不到函数内部变量的。
举个例子
function a() {
let i = 0;
}
console.log(i); // ReferenceError: i is not defined
在外部访问会报错,对吧,因为根据作用域链的规则,只能是函数内部能访问外部的变量,那我现在有一个需求,我就想访问内部的变量,怎么办,这个时候就可以使用闭包了。既然内部可以访问外部的变量,那我就在内部定义一个函数,去访问这个变量,然后我再把这个函数给返回出去不久可以了。
function a() {
let i = 0;
return function b() {
console.log(i);
}
}
let c = a();
c(); // 0
这个改造一下,这个就是闭包了
闭包到底会不会造成内存泄露呢
怎么说呢,这个问题,我觉得有可能会造成内存泄露,只是说有可能,如果在写代码过程中,我们无意识的使用了闭包,这个时候运行环境js引擎优化方面做的好的话会自动帮我们释放,我们无需担心,但有的并不会释放,当项目特别大的时候,电脑配置又不好的时候,就有可能造成内存泄漏了。但现在的js引擎优化都做的挺好,而且es6出来后,感觉一般的业务中场景都不太需要用到闭包,加上学习并正确的使用闭包,感觉没什么问题。
7种JS-IIFE(立即执行函数)写法
IIFE(Immediately Invoked Function Expressions)
叫做立即执行表达式,顾名思义,该表达式一被创建就立即执行。
1.对返回结果不进行处理
(function(形参){
函数体内容
})(实参);
2.对返回结果不进行处理
(function(形参){
函数体内容
}(实参));
3.返回的是一个布尔值,然后进行取反
!function(形参){
函数体内容
}(实参)
4.对于数字返回的是原来的结果,非数字返回NaN
+function(形参){
函数体内容
}(实参)
5.对于数字返回的是正负符号相反,非数字返回NaN
-function(形参){
函数体内容
}(实参)
6.对于数字返回的是正负符号相反再减1,非数字返回-1
~function(形参){
函数体内容
}(实参)
7.返回的结果是undefined
void function(形参){
函数体内容
}(实参)
IIFE立即执行函数
区别function
Iife最左边为(。编译器认为是iife,不是函数。
IIFE的目的是为了隔离作用域,防止污染全局命名空间
弥补scope的缺陷 用于隔离作用域
全局作用域 块级作用域 函数作用域
只有function才能实现作用域隔离,因此如果要将一段代码中的变量、函数等的定义隔离出来,只能将这段代码封装到一个函数中。
将代码封装到函数中的目的是为了复用。在JS中,当然声明函数的目的在大多数情况下也是为了复用,但是JS迫于作用域控制手段的贫乏,我们也经常看到只使用一次的函数:这通常的目的是为了隔离作用域了!既然只使用一次,那么立即执行好了!
IIFE构造单例模式
JS的模块就是函数,最常见的模块定义如下:
functionmyModule(){
varsomeThing="123";
varotherThing=[1,2,3];
functiondoSomeThing(){
console.log(someThing);
}
functiondoOtherThing(){
console.log(otherThing);
}
return{
doSomeThing:doSomeThing,
doOtherThing:doOtherThing
}
}
varfoo=myModule();
foo.doSomeThing();
foo.doOtherThing();
varfoo1=myModule();
foo1.doSomeThing();
如果需要一个单例模式的模块,那么可以利用IIFE:
var myModule=(functionmodule(){
varsomeThing="123";
varotherThing=[1,2,3];
functiondoSomeThing(){
console.log(someThing);
}
functiondoOtherThing(){
console.log(otherThing);
}
return{
doSomeThing:doSomeThing,
doOtherThing:doOtherThing
}
})();
myModule.doSomeThing();
myModule.doOtherThing()
IIFF(立即执行函数表达式)
立即执行函数表达式(Immediately-invoked function expression,IIFF)
在javascript(ES5)中,是没有块级作用域的概念的
for (var i = 0; i < 5; i++) {
}
console.log(i); //5
因为没有块级作用域的概念,因此,在 for 循环中声明的 i 变量实际上是一个全局变量,因此可以在全局环境中访问的到。
块级作用域,也可以称为私有作用域。也就是说只在for循环的语句块中有定义,一旦循环结束,变量 i 就会被销毁。而在ES5中, 我们主要通过匿名函数的方式来块级作用域。
用作块级作用域(私有作用域)的匿名函数的语法:
(function() {
//此处是块级(私有)作用域
})()
以上代码定义并立即调用了一个匿名函数。将函数声明包含在一对圆括号中,表示它实际上是一个函数表达式。
//函数名没意义,所以使用匿名函数
//第一个圆括号:将匿名函数转换为函数表达式。
//第二个圆括号:立即执行匿名函数
(function() {
console.log(123);
})()
//当然,你也可以给一个函数名,不过函数名在这里没有意义,因为整个函数在执行时就立即调用了。
(function keith() {
console.log(123);
})()
总结一下IIFE的优点: 1.创建块级(私有)作用域,避免了向全局作用域中添加变量和函数,因此也避免了多人开发中全局变量和函数的命名冲突 2.IIFE中定义的任何变量和函数,都会在执行结束时被销毁。这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数 的引用。只要函数执行完毕,就可以立即销毁其作用域链了。
今天关于js 立即执行函数 IIFE和js 立即执行函数的讲解已经结束,谢谢您的阅读,如果想了解更多关于# 立即执行函数(IIFE)和闭包、7种JS-IIFE(立即执行函数)写法、IIFE立即执行函数、IIFF(立即执行函数表达式)的相关知识,请在本站搜索。
本文标签: