JavaScript初阶--------函数闭包立即执行函数

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript初阶--------函数闭包立即执行函数相关的知识,希望对你有一定的参考价值。

 函数 

    有时候我们的代码重复了很多次,编程里面称为耦合,但是编程要讲究高内聚,弱耦合。为了将重复多的聚在一起就出现了函数。

定义

    函数基本要素:函数声明(function),函数名称,参数(形参,实参),返回值。

 

   1.首先函数命名方式采用小驼峰式写法,即第一个单词小写,后面的单词首字母大写,如 function oneNumber(){}

        2.函数表达方式里面有函数表达式,匿名函数表达式

 

var a = function lala() {}//函数表达式
var b = function () {}//匿名函数表达式

 

    为了便于使用以及方便,我们之后基本都是采用匿名函数表达式,并且称为函数表达式。

 

return返回值

   作用一:返回函数最终执行结果

   作用二:终止函数

 

 


  

函数作用域

    变量和函数生效的区域叫作用域,作用域分为全局作用域和局部作用域。访问时,里面的作用域可以访问外面的,外面的不能访问里面的。

   [[scope]]:每个javascript函数都是一个对象,对象中有些属性我们可以访问,但有些不可以,这些属性仅供javascript引擎存取,[[scope]]就是其中一个。

[[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合。

   作用域链:[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链。

   运行期上下文:当函数执行时,会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境,函数每次执行时产生对应的执行上下

文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,执行上下文被销毁。

 

 


 

预编译

      js运行三步:语法分析,预编译,开始执行

  暗示全局变量

     函数里面没有声明直接赋值的变量称为暗示全局变量,能够被全局调用,而且全局变量都是window上的属性,可以通过window.name来调用。

      预编译四部曲

        1.创建AO对象(执行上下文对象)

        2.找函数声明和函数中的变量声明,并赋值undefined

        3.赋值(形参和实参统一)

        4.函数体赋值给对应的AO属性

 

  举个栗子看看吧

    function fn(a){
        console.log(a);        // ?unction a(){ }
        var a = 123;
        console.log(a);        // 123
        function a(){ }      
        console.log(a);        //123
        console.log(b);        //undefined
        var b = function(){};      
        console.log(b);          //function () {}
        console.log(d);         // function d(){}
        function d(){}
    }
    fn(1);

 

 

 

      首先产生执行期上下文,然后找函数声明和变量声明a,b,d,下面是整个预编译过程。

        a --> undefined --> 1 --> function a ( ) { } --> 123

        b --> undefined --> function ( ) { }

        d --> undefined --> function d( ) { }   

      最后得出各AO属性对应的值

        function与var之间覆盖的问题,例如:

         

 function bar(){
    return foo;
    function foo(){ }
    var foo = 111
    }
    console.log(bar());   // 一开始就被return

 

  

 

 function bar(){
    foo = 10; 
    function foo(){ {;
    var foo = 11;
    return foo;
    }
    console.log(bar());     // 11 foo被赋值为11,最后才被return!!

 

  

 以上就是整个预编译过程

 

 


 

闭包

     说了那么多其实就是为了给闭包做铺垫,闭包会导致多个执行函数共用一个公有变量。所以一般如果不是特殊需要,尽量少用。容易污染全局变量。

举个栗子     

function a(){
   function b(){
   var bbb  = 234;
   console.log(aaa);
   }
   var aaa = 123;
   return b;
  }
  a();    //*****************************function b() {} 
var demo = a();
  demo(); 

 

  最后我们会发现,demo --> function b () { },demo() --> 123。这是因为当b执行完了相当于a也执行完了,这样a会把执行上下文销毁,但是b已经被return出去并且给了demo,当demo执行时也就是相当于function b () { }执行,得出123。这就是内存泄露,原有的作用域链不释放导致的。

     来个栗子巩固一下:

 function a() { 
      var aaa = 100;
      function b() {
          console.log(aaa);
          }
       return b;
    }
    var demo = a();
    demo(); //100  b的劳动成果已经保存到了demo里面

  当a函数定义的时候,产生一个执行上下文,里面有一个aaa,和一个函数b,当b定义的时候,已经含有a的劳动成果,意思就是它已经有a的执行上下文,并且在b执行的时候,产生它自己的执行上下文,最后当a函数执行完之后,把函数b返回到了全局作用域,虽然a执行完,并且销毁了它自己的执行上下文,但是因为其内部b函数的存在,仍然有a的全部执行上下文,所以,仍然可以通过demo来访问function a里面的aaa变量。

 

闭包的应用

  1.实现公有变量

     function add() {
         var num = 0;
         function demo(){
            num++;
            console.log(num);
            }
            return demo;
       }
      var test = add();
      test();//1
      test();//2

 

   对于上述栗子,公有变量就是num。add函数将demo函数返回出去,demo函数依然有add函数的执行上下文,每次执行test(),就相当于执行demo函数,每次访问的num都是同一个

num变量,这样num就是一个公有变量了,通过这种方式就能利用闭包产生一个累加器了。

 

       2.做缓存机构

  

 function test(){
           var num = 0;
           function a() {
              console.log(++num);
            }
           function b() {
              console.log(--num);
            }

            return [a,b];
    }
        var arr = test();
        arr[0]();//1
        arr[1]();//0

 

 

    a函数和b函数都被return到了外部,这样a函数和b函数都与num产生了一个闭包,并且a和b执行的都是同一个变量,当a改变num的时候,b的num也会发生改变,同理,b操作了

num,a的num也会发生改变,因为它们指向的num是同一个num,这就相当于一个缓存。

再来一个栗子

function eater() {
   var food = "";
   var obj = {
       eat : function() {
           if(food == "" ){
               console.log(‘empty‘);
               }else{
                   console.log("I am eating " + food);
                   food = "";
               }
           },
       push : function (myFood) {
               food = muFood
           }
       }
       return obj;
   }
   var eater1 = eater();
   eater1.eat();
   eater1.push(‘orange‘);
   eater1.eat();

 

    和上一个栗子一样,obj对象被return到了外部,并且用eater1来接收。eater1.eat()和eater1.push()所对应的food是同一个,这就是利用闭包产生缓存机构。

   

  3.私有化变量

      这个作用的了解需要先了解一下构造函数

      function Deng(){
                var prepareWife = "xiaozhang";
                var obj = {
                    name : "Laodeng",
                    age : 40,
                    sex : "male",
                    wife : "xiaoliu",
                    divorce : function () {
                        this.wife = delete this.wife;
                    },
                    getMarried :function () {
                        this.wife = prepareWife;
                    },
                    changePrepare : function (someone) {
                    preparewife = someone;
                        },
                    sayMywife : function (){
                        console.log(this.wife);
                    }
                }
                return obj ;
            }
            deng = Deng();

 

 

 

  

  运行一下看看

   

            deng.sayMyWife()         //"xiaoliu"
            deng.divorce()           //undefined (没有返回值)
            deng.sayMywife()           // true已经删除
            deng.changePrepare(‘xiaoxiaozhang‘)   //undefined (函数没有返回值)********
            deng.getMarried()        //undefined
            deng.sayMywife()         //"xiaoxiaozhang"
            deng.prepareWife         //undefined              

 

  prepareWife函数里面的变量,不在全局作用域里,所以我们访问不了,但是我们所有的操作都是围绕prepareWife来进行的,它们都可以正常访问这个变量,所以,像这种只能通过与

这个变量产生闭包的方法,属性,才能给对那个变量进行访问,所以,我们就称之为,私有化变量,我们可以通过定制接口(各种方法),来对变量的安全程度进行设置。

 

 

 

 


 

立即执行函数

    此类函数没有声明,在一次执行过后即释放。适合做初始化工作。比如有些数学公式,或者是其他一些常数的计算,我们没有必要把它一直放在全局的空间里,这样会很好内存,于

是就诞生了立即执行函数。

var x = (function(a, b){
                    return(a + b) ;
            }(1, 2)) // x = 3

 

   立即执行函数特点就是当JavaScript引擎解析到这个语句的时候就会马上执行,执行结束后马上把自己的执行上下文都销毁。这样就可以释放这里的内存,立即执行函数可以有返回值

以及形参等。

我们要知道函数声明不能执行,只有函数才能执行,所以

函数声明----->表达式

       function test() { }             // 函数声明,不是表达式

  var test = function () ;         // 函数表达式

  我们可以把函数声明转换成表达式

        1.+function test(){ }       -----> +号运算符,这样就将函数声明转换成表达式,就可以执行了

                           2. !function test(){ }       ------> !

          3. (function test(){ })( )         -------> ( ) 

 


 

 

利用立即执行函数解决闭包问题

     想要实现打印0-9,结果却出人意料。。。。。。。(出现了10个10)。这个我想了好久才想通了,唉,脑子不够用。。。

  function test() {
      var arr = [ ];
      for(var i = 0 ;i < 10; i++){
             arr[i] = function (){
                 console.log(i + ",");
                 }
              }
              return arr;
          }
      var demo = test();
      for(var j = 0; j < 10 ;j ++ ){
          demo[j]();
          }// 10 * 10

 

      这是为什么呢?输出的10个全是10,说明这里的i都是同一个i,为什么会这样呢,原来是function执行的时候i已经就是10了,由于test与arr之间产生了闭包,先这样说吧,每次return出

一个arr,但是function现在还没有执行,也就是都是arr[i],当终止循环的时候i=10,这时候function执行的时候10个i都是相加到10的那个i,是同一个i,所以最后打印出10个10。理解了

吗?感觉说的太通俗了,希望大家能够理解哈。

    

      如果觉得我说的有问题,欢迎提出来,大家一起进步哈!

 

  function test() {
         var arr = [];
         for(var i = 0; i < 10; i++){
            (function (j) {
                console.log(j);
                }(i))
             }
             return arr;
         }

 

利用立即执行函数,每次访问的i都不一样,所以打印出来的就是0-9了。

 

最后再来一个栗子

          a = 100 ;
          function demo(e) {
             function e() {}    
             arguments[0] = 2;
             document.write(e);   //2 因为形参列表将e改变为2
             if(a) {
                 var b = 123;
                 function c() { }
             }
             var c ;   
             a = 10 ;
             var a ;
             document.write(b);  // undefined
             f = 123;
             docuemnt.write(c);  //function (){}
             docuemnt.write(a); // 10
          }
          var a;
          demo(1)
          docuemnt.write(a);
          document.write(f);

 







以上是关于JavaScript初阶--------函数闭包立即执行函数的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript初阶---------- 数组

javascript(34)module模式立执行函数(Evernote)

JavaScript 闭包 立即执行函数

JavaScript 回调函数属于闭包?

JavaScript之闭包

javascript中的闭包以及闭包应用