博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaScript初阶(三)--------函数、闭包、立即执行函数
阅读量:4489 次
发布时间:2019-06-08

本文共 7562 字,大约阅读时间需要 25 分钟。

 函数 

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

定义

    函数基本要素:函数声明(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);

 

转载于:https://www.cnblogs.com/sunshinehu/p/7922582.html

你可能感兴趣的文章
spring boot
查看>>
浏览器URL传参最大长度问题
查看>>
学习进度条
查看>>
Linux crontab 定时任务详解
查看>>
string成员函数
查看>>
onSaveInstanceState()方法问题
查看>>
[转]CocoaChina上一位工程师整理的开发经验(非常nice)
查看>>
大数据时代侦查机制有哪些改变
查看>>
L1-047 装睡
查看>>
雷林鹏分享:jQuery EasyUI 菜单与按钮 - 创建链接按钮
查看>>
Apache Traffic Server服务搭建
查看>>
poj1990两个树状数组
查看>>
学习python-day1
查看>>
Zend_Db_Table->insert ()和zend_db_adapter::insert方法返回值不同
查看>>
递归问题
查看>>
Hyperledger下子项目
查看>>
Linq-查询上一条下一条
查看>>
常见前端开发的题目,可能对你有用
查看>>
BeautifulSoap库入门
查看>>
乐观锁与悲观锁
查看>>