js中的立即执行函数(function(){…})()

  • 引用

    最近在维护代码的时候,发现一段下面的代码,发现有几个不是很熟悉的知识点,先上代码:

setXy = function(x,y){
    return function()
    {
        window.midPort = {
            dataId:"",
            mode:"view",
            x:x,
            y:y
        };
        window.iframe_map.leader();
    }
}(a,b);

function lo()
{
    $(".jsframe").attr("src", 'zzz.html').load(function()
    {
        setXy();
    });

}
setTimeout(lo,400);

    代码中有几个知识点:

    1、出现了形如 function(x,y){}(a,b)的函数

    2、出现了调用函数是用到的函数名,未加括号

    从网上找资料知道,function(x,y){}(a,b)是立即执行的表达式,在解释这个之前,先了解一下几张函数的表现形式。

  • 关于函数声明、函数表达式、匿名函数

    函数声明:function fnName(){},使用关键字function声明一个函数,并指定一个函数名,叫函数声明。

    函数表达式:var fnName = function(){},使用function关键字声明一个函数,但是未给函数名,最后将匿名函数赋给一个变量,这叫函数表达式。

    匿名函数:和函数声明的区别是,没有给定函数名,它属于函数表达式。

    js引擎在解析js代码的时候,函数声明会得到提升,就是说具有优先解析权,在其他代码解析之前先解析,一会儿示例代码中,我们会看到。而函数表达式,就不会具有这种提升,在调用该函数之前,必须先定义这个函数表达式,否则会报错。

    下面我们先在在浏览器控制台里面写了一段代码,先调用tt函数,然后定义一个函数声明,结果是tt内的函数能被执行到。

    接下来我们定义一个函数表达式,在函数表达式定义之前调用(注意,执行之前在浏览器控制台清空缓存一下,否则之前定义的函数声明会被调用,或者定义的函数表达式和之前定义的函数声明的名字不要一样)

    结果是,后台报tt函数未定义,说明该函数表达式未被声明,验证了函数表达式在解析的时候是不会被提升的

    我们再在函数表达式定义之后调用,这是我们再开发的时候常用的套路,结果是能正常调用的:

  • 关于var fnName = function(){}()

    这种写法是说调用的时候立即执行,需要注意的是,这种写法是函数表达式才有的,没加括号和加括号到底有什么区别呢,我们代码演示一下:

function t1(){  
   alert("t1"); 
   t2(); 
}
var t2 = function(){
   alert("t2");
}()

    上面是说在调用的时候,立即执行,看看结果

    t1并没有弹出,是因为t1是函数声明,没办法立即执行,只有在调用了(形如t1())的时候才会弹出,t2弹出,说明在解析t1函数声明到t2();的时候,t2由于是立即执行,所以会弹出。

    如果现在调用t1(),我们知道会弹出 t1 ,但是会弹出 t2吗?我们就代码直接试一下吧,结果如下:

    可以得出一个结论,立即执行的函数是一次性的,后续不可以再调用了。

    下面我们再设置成不是立即执行的函数,效果和我们预想的是一样的,即函数只有在调用了才会弹出,调用t1弹出t1 ,调用t2,弹出t2;

function t1(){  
   alert("t1"); 
   t2(); 
}
var t2 = function(){
   alert("t2");
}

  上面我们的演示代码是用的普通的函数表达式的立即执行,我们知道,匿名函数也是一种函数表达式,那换成匿名函数怎么使用立即执行呢。

   由于匿名函数不能被调用,我们就直接写一个立即执行的匿名函数,代码如下:

(function(){alert('nimin')})()

    在控制台执行了这段代码以后,会立即弹出nimin弹出框,但是匿名函数后面没有加括号的话,不会执行,只是在后台打印了一个这个函数。

    我们注意到,在匿名函数外层加了一个括号 ,再在后面加括号,如果不在匿名函数外层加括号,用立即执行有效果吗,用下面代码:

function(){alert('nimin')}()

结果后台就报错了:

    这个错误是说在解析到(位置的时候,本来应该是一个函数名的,但是是一个(,导致语法错误,说明立即执行函数只能在函数表达式中生效。

那匿名函数立即执行还有哪些写法呢,下面是参考1 文中写的几种写法:

(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内部变量的作用。

  • 调用函数是用到的函数名,未加括号

     最后一个问题,调用函数加了括号会怎么样,我们前面说的立即执行,在这里是一样的道理,如果一个变量引用了另个函数表达式的话,加上括号以后就是立即执行,如果没有加括号的就是等函数被调用到的时候才会被执行,也就是定时器回调后才会执行,如果lo中加了括号,定时器就没有效果了。

reference:

  1. http://www.jb51.net/article/50967.htm (深入理解javascript中的立即执行函数(function(){…})())
  2. https://www.cnblogs.com/fangshidaima/p/Fangfang_chengzhang.html (JavaScript函数后面加不加括号的区别

猜你喜欢

转载自my.oschina.net/u/3470849/blog/1609323
今日推荐