GVKun编程网logo

js 立即执行函数 IIFE(js 立即执行函数)

22

如果您对js立即执行函数IIFE和js立即执行函数感兴趣,那么这篇文章一定是您不可错过的。我们将详细讲解js立即执行函数IIFE的各种细节,并对js立即执行函数进行深入的分析,此外还有关于#立即执行函

如果您对js 立即执行函数 IIFEjs 立即执行函数感兴趣,那么这篇文章一定是您不可错过的。我们将详细讲解js 立即执行函数 IIFE的各种细节,并对js 立即执行函数进行深入的分析,此外还有关于# 立即执行函数(IIFE)和闭包、7种JS-IIFE(立即执行函数)写法、IIFE立即执行函数、IIFF(立即执行函数表达式)的实用技巧。

本文目录一览:

js 立即执行函数 IIFE(js 立即执行函数)

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

fnName();

function fnName(){

    ...

}

//正常,因为‘提升’了函数声明,函数调用可在函数声明之前

 

fnName();

var fnName=function(){

    ...

}

//报错,变量fnName还未保存对函数的引用,函数调用必须在函数表达式之后

 

1

2

3

4

5

6

7

8

9

10

11

12

13

var fnName=function(){

    alert(''Hello World'');

}();

//函数表达式后面加括号,当javascript引擎解析到此处时能立即调用函数

function fnName(){

    alert(''Hello World'');

}();

//不会报错,但是javascript引擎只解析函数声明,忽略后面的括号,函数声明不会被调用

function(){

    console.log(''Hello World'');   

}();

//语法错误,虽然匿名函数属于函数表达式,但是未进行赋值操作,

//所以javascript引擎将开头的function关键字当做函数声明,报错:要求需要一个函数名


在理解了一些函数基本概念后,回头看看( 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(a){

    console.log(a);   //firebug输出123,使用()运算符

})(123);

 

(function(a){

    console.log(a);   //firebug输出1234,使用()运算符

}(1234));

 

!function(a){

    console.log(a);   //firebug输出12345,使用!运算符

}(12345);

 

+function(a){

    console.log(a);   //firebug输出123456,使用+运算符

}(123456);

 

-function(a){

    console.log(a);   //firebug输出1234567,使用-运算符

}(1234567);

 

var fn=function(a){

    console.log(a);   //firebug输出12345678,使用=运算符

}(12345678)

可以看到输出结果,在function前面加!、+、 -甚至是逗号等到都可以起到函数定义后立即执行的效果,而()、!、+、-、=等运算符,都将函数声明转换成函数表达式,消除了javascript引擎识别函数表达式和函数声明的歧义,告诉javascript引擎这是一个函数表达式,不是函数声明,可以在后面加括号,并立即执行函数的代码。

加括号是最安全的做法,因为!、+、-等运算符还会和函数的返回值进行运算,有时造成不必要的麻烦。

不过这样的写法有什么用呢?

javascript中没用私有作用域的概念,如果在多人开发的项目上,你在全局或局部作用域中声明了一些变量,可能会被其他人不小心用同名的变量给覆盖掉,根据javascript函数作用域链的特性,可以使用这种技术可以模仿一个私有作用域,用匿名函数作为一个“容器”,“容器”内部可以访问外部的变量,而外部环境不能访问“容器”内部的变量,所以( function(){…} )()内部定义的变量不会和外部的变量发生冲突,俗称“匿名包裹器”或“命名空间”。

JQuery使用的就是这种方法,将JQuery代码包裹在( function (window,undefined){…jquery代码…} (window)中,在全局作用域中调用JQuery代码时,可以达到保护JQuery内部变量的作用。

# 立即执行函数(IIFE)和闭包

# 立即执行函数(IIFE)和闭包

IIFE

IIFE: immediately-invoked function expression,立即调用函数表达式,函数在定义的时候,立即执行,是一种语法。

前置知识

了解函数声明和函数表达式的区别

  1. 以 function 开头的就是函数声明
  2. 痛过赋值给变量的就是函数表达式

例子:

function a() {} // 函数声明
var a = function () {}; //  函数表达式

形式

(function () {})(); // 形式1
(function () {}()); // 形式2
+function () {}();  // 形式3
-function () {}();  // 形式4
!function () {}();  // 形式5
~function () {}();  // 形式6
  1. 声明一个函数,一般是匿名函数,因为立即执行函数是不需要名字的
  2. 将函数声明转换成函数表达式,可以使用下面四个一元运算符(只用一个操作数):+ - ! ~(按位取反运算符),或者用小括号包裹起来
  3. 让表达式执行,在表达式后面添加小括号让函数立即执行

作用

  1. 不需要为函数命名,避免污染全局变量
  2. 创建一个独立作用域,这个作用域里面的变量外界访问不到,避免污染全局变量

使用场景

  1. 页面加载完后,需要立即执行一些初始化设置,例如事件处理,创建对象等
  2. 在应用中只执行一次的代码

闭包

这个知识点来来回回总结了好多次,主要自己没有一个太明确的概念,而且再工作中用的场景也很好,后面遇到再好好补充。

闭包:在函数外部访问函数内部的变量的函数,就是闭包,正常情况下在函数外部是访问不到函数内部变量的。

举个例子

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(立即执行函数)写法

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立即执行函数

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(立即执行函数表达式)

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 立即执行函数 IIFEjs 立即执行函数的讲解已经结束,谢谢您的阅读,如果想了解更多关于# 立即执行函数(IIFE)和闭包、7种JS-IIFE(立即执行函数)写法、IIFE立即执行函数、IIFF(立即执行函数表达式)的相关知识,请在本站搜索。

本文标签: