zoukankan      html  css  js  c++  java
  • 深入理解javascript中的立即调用的函数表达式(IIFE)

      在弄清楚什么是立即调用的函数表达式(IIFE)之前,我们先了解一些函数的基本概念。

      函数声明、函数表达式、匿名函数

        函数声明:function functionName() {...}; 使用function关键字声明一个函数,再指定一个函数名,叫函数声明。函数声明后不会立即执行,会在我们需要的时候调用到。

        函数表达式:var test = function () {…};使用function关键字声明一个函数,但未给函数命名,最后将匿名函数赋予一个变量,叫函数表达式,这是最常见的函数表达式语法形式。

        匿名函数:function () {}; 使用function关键字声明一个函数,但未给函数命名,所以叫匿名函数,匿名函数属于函数表达式,匿名函数有很多作用,赋予一个变量则创建函数,赋予一个事件则成为事件处理程序或创建闭包等等。

      函数声明和函数表达式不同之处在于

    1. Javascript引擎在解析javascript代码时会‘函数声明提升'(Function declaration Hoisting)当前执行环境(作用域)上的函数声明,而函数表达式必须等到Javascirtp引擎执行到它所在行时,才会从上而下一行一行地解析函数表达式
    2. 函数表达式后面可以加括号立即调用该函数,函数声明不可以,只能以 functionName()形式调用 。

       以下是两者差别的两个例子:  

    myFunction();
    function myFunction () {
        //code
    }
    //正常,因为‘提升'了函数声明,函数调用可在函数声明之前
    
    myFunction();
    var myFunction = function () {
        //code
    }
    //报错,变量myFunction还未保存对函数的引用,函数调用必须在函数表达式之后

      在JavaScript里,任何function在执行的时候都会创建一个执行上下文,因为为function声明的变量和function有可能只在该function内部,这个上下文,在调用function的时候,提供了一种简单的方式来创建自由变量或私有子function。

    // 由于该function里返回了另外一个function,其中这个function可以访问自由变量i
    // 所以说,这个内部的function实际上是有权限可以调用内部的对象。

    function makeCounter() {
    // 只能在makeCounter内部访问i
    var i = 0;

    return function () {
    console.log(++i);
    };
    }

    // 注意,counter和counter2是不同的实例,分别有自己范围内的i。

    var counter = makeCounter();
    counter(); // logs: 1
    counter(); // logs: 2

    var counter2 = makeCounter();
    counter2(); // logs: 1
    counter2(); // logs: 2

    alert(i); // 引用错误:i 没有defind(因为i是存在于makeCounter内部)。

      很多情况下,我们不需要makeCounter多个实例,甚至某些情况下,我们也不需要显示的返回值。

      有时需要在定义函数之后,立即调用该函数。这种函数就叫做立即执行函数,全称为立即调用的函数表达式IIFE(Imdiately Invoked Function Expression)

      javascript引擎规定,如果function关键字出现在行首,一律解释成函数声明语句

      【1】函数声明语句需要一个函数名,由于没有函数名,所以报错

    //SyntaxError: Unexpected token 
    function(){}();

      【2】函数声明语句后面加上一对圆括号,只是函数声明语句与分组操作符的组合。由于分组操作符不能为空,所以报错

    //SyntaxError: Unexpected token 
    function foo(){}();
    
    //等价于
    function foo(){};
    ();  //SyntaxError: Unexpected token

      【3】函数声明语句加上一对有值的圆括号,也仅仅是函数声明语句与不报错的分组操作符的组合而已

    function foo(){}(1);
    
    //等价于
    function foo(){};
    (1);

      所以,解决方法就是不要让function出现在行首,让引擎将其理解成一个表达式

      最常用的两种办法

    (function(){ /* code */ }()); 
    (function(){ /* code */ })(); 

      其他写法

    var i = function(){ return 10; }();
    true && function(){ /* code */ }();
    0, function(){ /* code */ }();
    
    !function(){ /* code */ }();
    ~function(){ /* code */ }();
    -function(){ /* code */ }();
    +function(){ /* code */ }();
    
    new function(){ /* code */ };
    new function(){ /* code */ }();

      自执行匿名函数和立即执行的函数表达式区别 

    // 这是一个自执行的函数,函数内部执行自身,递归
    function foo() { foo(); }
    
    // 这是一个自执行的匿名函数,因为没有标示名称
    // 必须使用arguments.callee属性来执行自己
    var foo = function () { arguments.callee(); };
    
    // 这可能也是一个自执行的匿名函数,仅仅是foo标示名称引用它自身
    // 如果你将foo改变成其它的,你将得到一个used-to-self-execute匿名函数
    var foo = function () { foo(); };
    
    // 有些人叫这个是自执行的匿名函数(即便它不是),因为它没有调用自身,它只是立即执行而已。
    (function () { /* code */ } ());
    
    // 为函数表达式添加一个标示名称,可以方便Debug
    // 但一定命名了,这个函数就不再是匿名的了
    (function foo() { /* code */ } ());
    
    // 立即调用的函数表达式(IIFE)也可以自执行,不过可能不常用罢了
    (function () { arguments.callee(); } ());
    (function foo() { foo(); } ());

      用途

       javascript中没用私有作用域的概念,如果在多人开发的项目上,你在全局或局部作用域中声明了一些变量,可能会被其他人不小心用同名的变量给覆盖掉

       根据javascript函数作用域链的特性,可以使用这种技术可以模仿一个私有作用域,用匿名函数作为一个“容器”,“容器”内部可以访问外部的变量,而外部环境不能访问“容器”内部的变量,

       所以( function(){…} )()内部定义的变量不会和外部的变量发生冲突,俗称“匿名包裹器”或“命名空间”。

     

      假设有一个需求,每次调用函数,都返回加1的一个数字(数字初始值为0)

      【1】全局变量:一般情况下,我们会使用全局变量来保存该数字状态

    var a = 0;
    function add(){
        return ++a;
    }
    console.log(add());//1
    console.log(add());//2
    但上面的方法中,变量a实际上只和add函数相关,却声明为全局变量,不太合适。

      【2】自定义属性:将变量a更改为函数的自定义属性更为恰当

    function add(){
        return ++add.count;
    }
    add.count = 0;
    console.log(add());//1
    console.log(add());//2
    其实这样做,还是有问题。有些代码可能会无意中将add.count重置

      【3】立即调用的函数表达式(IIFE):使用IIFE把计数器变量保存为私有变量更安全,同时也可以减少对全局空间的污染

    var add = (function(){
        var counter = 0;
        return function(){
            return ++counter; 
        }
    })();
    console.log(add())//1
    console.log(add())//2
  • 相关阅读:
    C# 获取类似java gettime() 的时间格式
    LUbuntu电脑棒安装指南
    Visual Studio Gallery
    SQL SERVER 分页存储过程
    asp.mvc获取checkbox、radio、select的值
    C#面向对象的一些笔记
    Javascript预解析、作用域、作用域链
    解决ajax请求cors跨域问题
    Asp.Net操作WebServices
    2019年科技趋势前10位
  • 原文地址:https://www.cnblogs.com/zhang-xun/p/6769970.html
Copyright © 2011-2022 走看看