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

     2022-03-19     143

关键词:

 函数 

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

定义

    函数基本要素:函数声明(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中,闭包恐怕是很多人不能理解的一个概念了,甚至很多人也会把闭包和匿名函数混淆。  闭包是有权访问另一个函数作用域中的变量的函数。首先要明白的就是,闭包是函数。由于要求它可以访... 查看详情

javascript初阶----------数组

数组  数组的定义    1.数组字面量(直接量)vararr=[1,2,3];    2.利用构造函数newArray(length/content)        vararr=newArray(1,2,3,4);arr=[1,2,3,4]           如果只传一位参数就代... 查看详情

javascript函数闭包(closure)

一,首先感受下javascript函数的闭包 二,闭包1,定义:闭包就是能够读取其他函数内部变量的函数,由于在javascript语言中,只有在函数内部的子函数才能够读取局部变量,因此可以把闭包简单的理解成:定义在一个函数内部... 查看详情

javascript闭包

...函数的所有变量,即使外层函数已经执行完毕(这点涉及JavaScript作用域链)。由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。所以,在本质上,闭包... 查看详情

javascript闭包立即执行函数(代码片段)

JavaScript闭包文章目录JavaScript闭包一、为什么要闭包二、让某些变量得以常驻内存1.原理2.Why立即执行函数?三、让外部得以访问函数内变量四、立即执行函数总结#前言-什么是闭包函数闭包函数是声明在另一个函数内的函数,是被... 查看详情

javascript闭包

转自:http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html闭包的概念:各种专业文献上的"闭包"(closure)定义非常抽象,很难看懂。闭包就是能够读取其他函数内部变量的函数。由于在Javascript语言中,只有函数内部的子函... 查看详情

javascript闭包和回调详解

一、闭包 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。闭包有三个特性:1.函数嵌套函数;2.函数内部可以引用外部的参数和变量;3.参数和变量不会被垃圾回收机制回收。 闭... 查看详情

javascript闭包

作者:刘志祥时间:2017.11.9 闭包一直作为javaScript的重点和难点,一般刚入门都无法深入的理解闭包,只是能了解其皮毛。在专业文献中"闭包"(closure)定义非常抽象,很难看懂。粗略的理解,闭包就是能够读取其他函数内... 查看详情

javascript函数之------------------闭包

   谈到闭包,人们常常会把匿名函数和闭包混淆在一起。闭包是指由权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数,仍以前面的createComparisonFunction()函数为例 ... 查看详情

javascript闭包详解

一、什么是匿名函数创建一个函数并将它赋值给变量functionName,这种情况下创建的函数,即匿名函数。(函数表达式就是匿名函数) 二、闭包1.什么是闭包?闭包就是能够读取其他函数内部变量的函数。只有函数内部的子函... 查看详情

javascript闭包

...作用域的变量的函数 只要存在调用内部函数的可能,JavaScript就需要保留被引用的函数。而且JavaScript运行时需要跟踪引用这个内部函数的所有变量,直到最后一个变量废弃,JavaScript的垃圾收集器才能释放相应的内存空间 ... 查看详情

javascript闭包函数详解(代码片段)

...的概念闭包的用途闭包的缺点闭包函数变量作用域要理解JavaScript闭包,就要先理解JavaScript的变量作用域。变量的作用域有两种:全局的和局部的(全局变量和局部变量)JavaScript中,在函数内部可以直接读取... 查看详情

javascript——闭包

<script>  /*闭包概念:    当一个内部函数被调用,就会形成闭包,闭包就是能够读取其他函数内部变量的函数,    定义在一个函数内部的函,创建一个闭包环境,让返回的这个子程序抓住i,以便在后续执行时... 查看详情

javascript闭包函数

1、匿名函数2、闭包3、举例4、注意1、匿名函数函数是JavaScript中最灵活的一种对象,这里只是讲解其匿名函数的用途。匿名函数:就是没有函数名的函数。1.1函数的定义,首先简单介绍一下函数的定义,大致可分为三种方式第一... 查看详情

javascript的闭包详解

(1)定义:  函数内部返回一个函数,返回出来的这个函数叫做被我们称之为闭包(个人理解的最简单的表现形式,)(2)为什么要使用闭包呢?局部变量在函数执行完之后就会被GC回收,有时候我们想在外部访问内部的变量,这个时候... 查看详情

javascript闭包

闭包就是能够读取其他函数内部局部变量的函数。闭包就是将函数内部和函数外部连接起来的一座桥梁。1.概念:允许使用内部函数,并且这些内部函数设置去访问它们所在的外部函数中声明的所有局部变量、其他内部函数。&nbs... 查看详情

javascript闭包如何实现函数的重载?

本文和大家分享的主要是妙用javascript闭包实现函数重载相关内容,一起来看看吧,希望对大家学习javascript有所帮助。1.准备知识1.1闭包闭包是一个函数在创建时,允许该自身函数访问并操作该自身函数以外的变量时所创建的作... 查看详情

javascript函数,作用域以及闭包

JavaScript函数,作用域以及闭包1. 函数(1).函数定义:函数使用function关键字定义,它可以用在函数定义表达式或者函数声明定义。a.函数的两种定义方式:*functionfunctionName(){}*varfunctionName=function(){}b.两种函数定义不同之处1).... 查看详情