美文网首页
JS立即执行函数的写法

JS立即执行函数的写法

作者: 年轻人多学点 | 来源:发表于2021-02-09 00:52 被阅读0次

    一、JS立即执行函数的写法

    方式1、最前最后加括号

    (function(){alert(1);}()); 
    

    方式2、function外面加括号

    (function(){alert(1);})(); 
    

    方式3、function前面加运算符,常见的是!与void

    !function(){alert(1);}(); 
    void function(){alert(2);}(); 
    

    二、立即执行函数的参数

    可以给立即执行函数传递参数,例如

    (function(who, when) {
        console.log("I met " + who + " on " + when);
    } ("Joe Black", new Date()));
    

    记住:

    1、立即函数内部是可以访问外部变量的,所以很多情况下,我们并不需要传参数。如:jQuery的window实参,如果不传入。内部也是可以直接使用的。

    2、通常你不应该给立即执行函数传递太多的函数,因为它很快会成为一个负担——为了理解代码是如何工作的,你不得不经常上下滚动源代码。

    三、立即执行函数的返回值

    像其它任何函数一样,一个立即执行函数也能返回值并且可以复制给其它变量,例如

    var result = (function () {
        return 2 + 2;
    }());
    
    var result = (function () {
        return 2 + 2;
    })();
    

    四、立即执行函数的好处

    1、立即执行函数模式被广泛使用,它可以帮你封装大量的工作而不会在背后遗留任何全局变量。

    2、定义的所有变量都会成员立即执行函数的局部变量,所以你不用担心这些临时变量会污染全局空间。

    3、这种模式经常被使用在书签工具(bookmarklets)中,因为书签工具在任何页面上运行并且保持全局命名空间干净是非常必要的;

    4、这种模式也可以让你将独立的功能封装在自包含模块中。

    5、可以将这些代码封装进一个立即执行函数中,并且确保页面没有它的情况下也能正常工作。

    6、可以添加更多的加强模块,移除它们,单独测试它们,允许用户去禁用它们等等。

    五、注意点

    立即执行函数通常作为一个单独模块使用。一般没有问题,但是,建议在自己写的立即执行函数前加分号,这样可以有效地与前面代码进行隔离。否则,可能出现意想不到的错误。

    例如

    var c=12
    var d=c
    (function (){}(
    console.log()
    ))
    

    会报这样一个错误:

    • 立即执行函数,就是在定义函数的时候直接执行,这里不是申明函数而是一个函数表达式

      1.问题

      在javascript中,每一个函数在被调用的时候都会创建一个执行上下文,在函数内部定义的变量和函数只能在该函数内部调用,正是因为这个上下文,使得在调用函数的时候可以创建一些私有变量。如下代码

    //makeCounter,返回一个新的函数(闭包),这个函数可以访问makeCounter里的局部变量i
            function makeCounter() { var i = 0; return function () {
                document.write(++i);
                document.write('<br>');
              }
            } //counter1和counter2是不同的实例,分别拥有自己范围内的变量i
            var counter1 = makeCounter();
            counter1();
            counter1(); var counter2 = makeCounter();
            counter2();
            counter2();
    
    这里i是函数makeCounter函数内的局部变量,所以定义的counter1和counter2都有自己的变量i,上面代码输出结果如下:
    
    image.png
    注意闭i始终保存在内存中,所以第二次调用的时候输出的是2。
    
    普通情况下我们定义一个函数,然后在语句中函数名字后面加上一对圆括号就可以直接调用它,能不能定义完之后直接在后面加上小括号调用呢?如下
    
    function(){ counter1(); }(); // SyntaxError: Unexpected token (
    
    答案是不行,这样会报错的。为什么呢?在javascript解释代码的时候,遇到function关键字的时候就认为这里是一个**函数声明**,**而不是函数表达式**,如果没有显式地定义成函数表达式就会报错,因为**函数声明需要一个函数名**,上面的代码没有函数名。
    
    既然是因为没有函数名字报错那好就加上一个函数名,如下:
    
    function foo(){ counter1(); }(); // SyntaxError: Unexpected token )
    
    依然会报错,为什么呢?在一个函数声明语句(这次是正确的)后面加上一对圆括号,这对圆括号和前面的声明语句没有任关系,而只是一个**分组操作符**,用来控制运算的优先级,这里的意思是小括号里面优先计算,所以上面代码等同于:
    
    function foo(){ counter1(); }
        (); // SyntaxError: Unexpected token )
    
    **2.概念**
    
    正确的写法是怎样的呢?简单,如下:
    
    (function () { counter1(); }());
    
    这样为什么就可以呢?在javascript里圆括号内不能包含语句,**当解释器对代码进行解释的时候遇到圆括号就认为这里面是表达式,然后遇到function关键字就认为这是一个函数表达式,而不是函数声明**。而更加奇妙的是只要是能将后面语句预先解释为表达式都可以,不一定是分分组操作符,于是立即执行函数表达式有了五花八门的写法,如下:
    
    (function () { counter1(); }());
            (function () { counter1(); })(); var i = function(){ counter1(); }(); true && function () { counter1(); }(); 0, function(){ counter1() }(); !function () { counter1(); }(); ~function () { counter1(); }(); -function () { counter1(); }(); +function () { counter1(); }();
    
    输出结果如下:
    
    image.png
    甚至可以这样:
    
    new function(){ counter1(); } new function(){ counter1(); }() // 带参数
    
    这样:
    
     var i = function(){ counter1(); }(); var j = (function(){ return 10; }());
    
    这是为什么呢?因为new,=是运算符,和+,-,*,/一个样,都会把后面的语句预先解释为表达式。这里推荐上面一种写法,因为function内部代码如果太多,我们不得不滚到最后去看function(){}后是否带有()。
    
    **3.立即执行函数和闭包有什么关系**
    
     和普通函数传参一样,立即执行函数也可以传递参数。如果在函数内部定一个函数,而里面的那个函数能引用外部的变量和参数(闭包),我们就能用立即执行函数锁定变量保存状态。
    
    我们在hmtl页面中方两个超链接标签,然后用下面的代码来测试:
    
    <div>
            <ul>
                <li><a>第一个超链接</a></li>
                <li><a>第二个超链接</a></li>
            </ul>
        </div>
            var elems = document.getElementsByTagName('a'); for(var i=0; i<elems.length; i++) {
              elems[i].addEventListener('click', function (e) {
                e.preventDefault();
                alert('I am click Link #' + i);
              }, 'false')
            }
    
    这段代码意图是点击第一个超链接提示“I am click Link #0”,点击第二个提示“I am click Link #1”。真的是这样吗? 不是,每一次都是“I am click Link #2”
    
    image.png
    因为i的值没有被锁住,当我们点击链接的时候其实for循环早已经执行完了,于是在点击的时候i的值已经是elems.length了。
    
    修改代码如下:
    
    var elems = document.getElementsByTagName('a'); for(var i=0; i < elems.length; i++){
              (function (LockedInIndex) {
                elems[i].addEventListener('click', function (e) {
                  e.preventDefault();
                  alert('I am cliick Link #' + i);
                }, 'false')
              })(i)
            } 
    
    这次可以正确的输出结果,i的值被传给了LockedIndex,并且被锁定在内存中,尽管for循环之后i的值已经改变,但是立即执行函数内部的LockedIndex的值并不会改变。
    
    还可以这样写:
    
    var elems = document.getElementsByTagName('a'); for ( var i = 0; i < elems.length; i++ ) {
              elems[ i ].addEventListener( 'click', (function( lockedInIndex ){ return function(e){
                  e.preventDefault();
                  alert( 'I am link #' + lockedInIndex );
                };
              })( i ), 'false' );
            }
    
    但是我觉得如果用let是不是就可以一下子解决了:
    
      var elems = document.getElementsByTagName('a'); for(let i=0; i<elems.length; i++) {
              elems[i].addEventListener('click', function (e) {
                e.preventDefault();
                alert('I am click Link #' + i);
              }, 'false')
            }
    
    let是块级作用域内的变量,是es6新定义的,这里不展开。
    
    4.模块模式
    
    立即执行函数在模块化的时候也有用,**用立即执行函数处理模块可以减少全局变量造成的空间污染**,而是使用私有变量。
    
    如下创建一个立即执行的匿名函数,该函数返回一个对象,包含要暴露给外部的属性i,如果不实用立即执行函数就要多定义一个属性i了,这个i就会显示的暴露给外部,这样:counter.i,这种方式明显不太安全。
    
     var counter = (function(){ var i = 0; return {
                get: function(){ return i;
                },
                set: function( val ){
                  i = val;
                },
                increment: function() { return ++i;
                }
              };
            }());
            document.write('<br>');
            document.write(counter.get());document.write('<br>');
            document.write(counter.set( 3 ));document.write('<br>');
            document.write(counter.increment());document.write('<br>'); // 4
            document.write(counter.increment());document.write('<br>'); // 5
    
    注意,这里如果使用counter.i来访问这个内部变量,会报错undefined,因为i并不是counter的属性。
    
    好了,就这么多。
    

    相关文章

      网友评论

          本文标题:JS立即执行函数的写法

          本文链接:https://www.haomeiwen.com/subject/tjrqxltx.html