zoukankan      html  css  js  c++  java
  • 【转】 前端笔记之JavaScript(四)关于函数、作用域、闭包那点事

    【转】 前端笔记之JavaScript(四)关于函数、作用域、闭包那点事

    一、自定义函数function

    函数就是功能、方法的封装。函数能够帮我们封装一段程序代码,这一段代码会具备某一项功能,函数在执行时,封装的这一段代码都会执行一次,实现某种功能。而且,函数可以多次调用。

    1.1函数的定义和调用

    语法:

    定义:把需要实现的功能预先做好

    执行:需要的时候执行这个功能,而且还可以执行多次

    定义:function myName(){}

    执行:myName()

    【语法解释】:

     function  定义函数的关键字

     myName    函数名称

     ()         参数集

     {}        函数体,执行的代码都放在{}里面

    多条语句,组成一个“语句军团”,集体作战。

    //定义一个函数,函数就是一组语句的集合
    function haha(){
       console.log(1);
       console.log(2);
       console.log(3);
       console.log(4);
    }
    haha();//调用haha函数
    haha();//调用haha函数
    haha();//调用haha函数

    函数必须先定义,然后才能调用

    定义一个函数,用关键字function来定义function就是英语“功能”的意思。表示这里面定义的语句,完成了一些功能。function后面有一个空格,后面就是函数名字,函数的名字也是关键字,命名规范和变量命名是一样的。名字后面有一对儿圆括号,里面放置参数。然后就是大括号,大括号里面是函数的语句。

     function 函数名称(){

     

     }

    函数如果不调用,里面的语句一辈子都不执行,等于白写。

    调用函数的方法,就是函数名称加()()是一个运算符,表示执行一个函数。

     函数名称()

    一旦调用函数,函数内部的代码不管对错,都会执行。

    能感觉到,函数是一些语句的集合,让语句称为一个军团,集体作战。要不出动都不出动,要出动就全动。

    函数的意义1:在出现大量程序代码相同时候,可以为它门封装成一个function,这样只调用一次,就能执行很多语句。


    1.2函数的参数

    定义在函数内部的语句,都是相同的,但是实际上可以通过“参数”这个东西,来让语句有差别。

    定义函数时,内部语句可能有一些悬而未决的量,就是变量,这些变量,要求在定义时都罗列在圆括号中:

    function fun(a){
       console.log("我第"+a+"次说你好!");
    }
    fun(100);
    fun(1);
    fun(2);
    fun(3);

    调用的时候,把这个变量真实的值,一起写在括号里,这样随着函数的调用,这个值也传给了a变量参数。

    罗列在function圆括号中的参数,叫做形式参数;调用时传递的数值,叫做实际参数。

     

    参数可以有多个,用逗号隔开。

    function sum(a,b){
       console.log(a + b);
    }
    sum(3,5);     //8
    sum(8,11);    //19
    sum("5",12);  //512
    sum(10);      //NaN,因为a的值是10,b没有被赋值是undefined,10+undefined=NaN
    sum(10,20,30,40,88); //30,后面的参数没有变量接收

    函数的意义2:在调用函数时,不用关心函数内部的实现细节,甚至这个函数是你网上抄的,可以运行。所以这个东西,给我们团队开发带来了好处。

     

    定义函数的时候,参数是什么类型,不需要指定类型:

    调用的时候,传进去什么类型,ab变量就是什么类型

     sum("5",12);  //512,做的是连字符串运算

    另外,定义和调用的时候参数个数可以不一样多,不报错。

     sum(10);      

    因为只给a变量赋值,b没有被赋值,b被隐式的varb的值是undefined10+undefined=NaN

     

     sum(10,20,30,40,88); //30

    只有前面两个参数被形参变量接收了,后面的参数没有变量接收,就被忽略了。

    //封装一个函数,计算m+....+n的和
    //比如m是4,n是15,4+5+6+7...+15
    function sum(m,n){
       var s = 0; //累加器,累加和
       for(var i = m;i <= n;i++){
           s+=i //s = s + i;
       }
       console.log(s);
    }
    sum(1,100); //计算1到100的和
    sum(10,13); //计算10+11+12+13的和
    sum(13,10); //输出0,所以你就知道函数顺序关机,定义顺序是什么,传递顺序就是什么。

    1.3函数的返回值

    函数可以通过参数来接收东西,还可以通过return关键字来返回值,“吐出”东西。

    function sum(a,b){
       return a+b; //现在这个函数的返回值就是a+b的和
    }
    
    //sum没有输出功能,就要用console.log输出
    console.log(sum(3,8));       //计算sum(3,8);实际上称为表达式,需要计算,计算后是11
    console.log(sum(3,sum(4,5)));//输出12,实际上两次执行了sum函数,先执行内层的,计算出9,然后sum(3,9)就是12

    函数有一个return的值,那么现在这个函数,实际上是一个表达式,换句话说这个函数就是一个值。

    所以这个函数,可以当做其他的函数参数。

     sum(3,sum(4,5));

    程序从内层执行到外层,sum(3,9)

    函数可以接收很多值,但是返回一个值。

    函数的意义3:模块化编程,让复杂的逻辑变得更简单。

    函数只能有唯一的return,有if语句除外。

    程序遇见return,会做两件事:

    1、立即返回结果,返回到调用它的地方

    2、不执行return后面的代码。

    function fun(){
       console.log(1);
       console.log(2);
       //return; //返回一个空值,undefined
       return "你好啊!";
       console.log(3); //这行语句不执行,因为函数已经return,所以会终止执行后面的代码。
    }
    console.log(fun();


    1.4函数模块化编程

    实现前提:函数有返回值,可以作为其他函数执行时传的实参。

    习惯将复杂工作,进行一步步的分工,将一部分工作的结果作为下一步工作的条件。

    将程序中某个单独的功能制作成单独函数,这就是造轮子的过程。

    业务逻辑上:将所有的轮子进行拼装。

    将程序分成有层次的模块,制作过程中一部分函数要有返回值,执行结果作为另一些模块的参数、条件。

    现在做一个程序,输出2~100的所有质数,所谓的质数,就是只有1和原数本身两个约数,没有其他约数。

    把一个复杂的问题,拆分成一个个小问题,每个都是一个单独的函数:

    逻辑思维:约数个数函数 → 判断质数函数 → 高层业务

    编程需要逆向思维编程:制作约数个数函数 → 制作判断质数函数 → 高层业务

    函数思维找质数:

    //约数个数函数:能够传入一个数字,返回它的约数个数
    function yueshugeshu(a){
       //计算这个数字的约数个数
       var count = 0; //累加约数个数
       for(var i = 1;i <= a;i++){
           if(a % i == 0){ //判断是否为约数
               count++;
           }
       }
       return count;
    }
    //判断是否是质数,如果一个函数名字取名为is,就暗示了将返回布尔值
    //要么返回true,要么返回false。
    //接收一个参数m,返回是否为质数(true或false)
    function isZhishu(m){
       if(yueshugeshu(m) == 2){
           return true;
       }else{
           return false;
       }
    }
    //寻找1~100的质数
    for(var i = 1;i <= 100; i++){
       if(isZhishu(i)){ //可以省略==true的判断
           //isZhishu()给我们返回了true和false
           console.log(i);
       }
    }

    利用函数验证哥德巴赫猜想:用户输入偶数拆分两个质数和:

    哥德巴赫猜想:任何一个偶数,都可以拆分为两个质数的和。

    现在要求,用户输入一个偶数,你把所有的质数拆分可能,写出来。

    比如:

    4 = 2 + 2

    6 = 3 + 3

    8 = 3 + 5

    48 = 5 + 43

    代码见案例:

    约数个数函数,里面的细节不需要关心,它足够的鲁棒,就能返回约数个数。

    上层的函数,可以使用下层的API

    //约数个数函数:能够传入一个数字,返回它的约数个数
    function yueshugeshu(a){
       //计算这个数字的约数个数
       var count = 0; //累加约数个数
       for(var i = 1;i <= a;i++){
           if(a % i == 0){ //判断是否为约数
               count++;
           }
       }
       return count;
    }
    //判断是否是质数,如果一个函数名字取名为is,就暗示了将返回布尔值
    //要么返回true,要么返回false。
    //接收一个参数m,返回是否为质数(true或false)
    function isZhishu(m){
       if(yueshugeshu(m) == 2){
           return true;
       }else{
           return false;
       }
    }
    //哥德巴赫猜想,用户输入一个数字
    //验证偶数是否能被拆分两个质数的和,拆分的思想就是穷举法
    //比如用户输入48,那么就:
    //看看1、47是不是都质数
    //看看2、46是不是都质数
    //看看3、45是不是都质数
    //...
    var even = parseInt(prompt("请输入一个偶数"));
    for(var i = 4;i < even;i++){
       if(isZhishu(i) && isZhishu(even - i)){
           console.log(even + "可以拆分为" + i + "与" + (even - i) + "的和");
       }
    }

    利用函数验证哥德巴赫猜想-一百万以内的偶数拆分:优化

    function yueshugeshu(a){
        ...
    }
    function isZhishu(m){
        ...
    }
    //注意验证,验证偶数能否被拆成两个质数
    waiceng:for(var i = 4 ; i <= 1000000 ; i+=2){
        for(var j = 2 ; j < i ; j++){
            if(isZhishu(j) && isZhishu(i - j)){
                console.log(i +"可以拆分为"+  j +"与"+  (i - j) + "的和");
                continue waiceng;
            }
        }
    }

    1.5函数递归

    函数可以自己调用自己,就是递归。

    function haha(){
       console.log("哈哈");
       haha();//调用自己
    }
    haha();
    function sum(a){
       if(a == 1){
           return 1;
       }else{
           return a + sum(a-1); //10 + 9 + 8 + sum(7)
       }
    }
    console.log(sum(10));

    斐波那契数列就是经典的递归算法:

    1 1123581321345589144233...

    输出斐波那契数列

    只需要一个函数,就可以搞定全部问题。

    fib(n); 就能得到第n位的数字

    fib(2) = 1

    fib(3) = 2

    fib(4) = 3

    fib(5) = 5

    ...

    fib(10) = 55

    function fib(n){
       if(n == 1 || n == 2){
           return 1;
       }else{
           return fib(n - 1) + fib(n - 2);
       }
    }
    // console.log(fib(10));
    
    for(var i = 1;i <= 50;i++){
       console.log(fib(i));
    }

    1.6函数表达式

    定义函数除了使用function之外,还有一种方法,就是函数表达式。就是函数没有名字,称为“匿名函数”,为了今后能够调用它,我们把这个匿名函数,直接赋值给一个变量。

    var haha = function(){
       console.log("哈哈");
    }
    // console.log(haha);
    haha(); //以后要调用这个函数,就可以直接使用haha变量调用。

    等价于:

    function haha(){
       console.log("哈哈");
    }
    haha();

    如果这个函数表达式的function不是匿名,而是有名字的:

    var haha = function xixi(){
       console.log("哈哈");
    }
    xixi();  //这是错误的
    haha();  //这的对的

    那么JS表现非常奇怪,在外部只能用haha()调用,xixi()会引发错误。

    也就是说,JS这个奇怪的特性,给我们提了个醒,定义函数,只能用以下两种方法,不能杂糅:

     function haha(){}

     var haha = function(){}

    错误的:

    var haha = function xixi(){}


    1.7函数声明的提升(预解析)

    //先调用,可以输出,因为有函数声明提升的特性
    fun();
    fun();
    fun();
    //后定义
    function fun(){
       console.log("我是函数!");
    }

    不会报错。

    JS在执行前,会有一个预解析的过程,把所有的函数声明和变量的声明,都提升到了最开头,然后再执行第一行代码。所以function定义在哪里,都不重要,程序总能找到这个函数。

    函数声明头可以提升,JS程序执行前,都会有一个函数预解释阶段,预解释阶段是自动进行的

    函数优先:函数声明和变量声明都会被提升,但是面试常考的一个细节是:函数会被首先提升,然后才是变量。

    函数提升是没节操的,无视if等语句的判断,强制提升

    JavaScript世界中,函数是一等公民。

    函数声明会被提升,但是函数表达式却不会被提升:

    fun();
    var fun = function(){  //因为它是函数表达式,而不是function定义法
       alert("我是函数!");
    }

    又给我们提了个醒,没有特殊的理由,都要用function haha(){}来定义函数。

    函数优先:

    aaa(); //现在这个aa到底是函数,还是变量5?
    console.log(aaa);//函数优先,遇见同名的标识符,预解析阶段一定把这个标识符给函数
    var aaa = 5; //定义一个变量,是5
    function aaa(){
       alert("我是aaa函数!")
    }

    面试题:

    函数优先,现在foo这个标识符冲突了,一个函数叫foo,一个变量也叫foo。预解析阶段,如果遇见标识符冲突,这个标识符给函数。

     


    1.8函数是一个引用类型

    基本类型:NumberStringBooleanundefinednull

    引用类型:ObjectfunctionarrayRegExpMathDate

    function fun(){}
    var haha = function (){}
    
    console.log(typeof fun); //引用类型中的function类型
    console.log(typeof haha);//引用类型中的function类型

    函数也是一种类型,这个类型叫function,是引用类型的其中一种。

    基本类型:保存值

    引用类型:保存地址

    现在变量a = 1,那么这个a变量里面存储1这个数字

    //基本类型的赋值
    var a = 1;
    var b = a; //b得到的值是a的副本,a把自己复制了一份,给了b
    b = 3;     //改变了b的值,a不受影响
    console.log(a); //1
    console.log(b); //3
    //引用类型的赋值:
    //定义了一变量a,引用了一个function
    //这个a变量存储的是这个匿名函数的内存地址
    var a = function(){
       alert("我是一根函数");
    }
    var b = a;  //就是把匿名函数的地址也给了b。
    b.xixi = 1; //给b添加一个属性
    console.log(a.xixi); //输出a的xixi属性,a也有这个属性了
    //b的xixi属性和a的变量都改变了,因为都是指向同一个对象(同一个内存地址)
    b.xixi++;
    b.xixi++;
    b.xixi++;
    console.log(a.xixi);
    console.log(b.xixi);


    总结:

    预解释:在js中,代码从上到下执行之前,(浏览器默认)首先会把所有带varfunction关键字的进行提前声明或者定义

    var num=88;

    声明(declare):相当于种树时"挖坑"  var num; 只声明没有定义时,num的默认值是undefined

    定义(defined):相当于种树时"栽树"  num=88;(给变量赋值)

    在预解释的时候,带var和带function的还不一样:

    var:只是提前的声明(定义赋值的部分是在代码执行的时候完成的)

    function:提前的声明+定义

    在浏览器加载HTML页面时,首先会开辟一个供js代码执行的环境-->"全局作用域"(window/global)

    栈内存(作用域):存储基本数据类型的值;提供js代码执行的环境;

    堆内存:在js中,对于引用数据类型来说,首先会开辟一个新的内存空间,然后把代码存储到这个空间中,最后把空间的地址给相关的变量--->我们把新开辟的这个内存空间称为"堆内存"

    堆内存的作用:存储引用数据类型值


    二、作用域

    2.1函数能封闭住作业域

    变量的作用域无非就两种:全局变量和局部变量。

    2.1.1全局变量(全局作用域)

    全局变量:在最外层函数定义的变量拥有全局作用域,即对任何内部函数来说,都是可以访问的。

    言外之意:如果变量没有定义在任何的function中,那么它将在程序中任意范围内都有定义:

    var a = 100; //定义在全局的变量,在程序任何一个角落都有定义
    function fn(){
       console.log("我是函数里面的语句,我认识全局变量a值为:" + a);
    }
    fn();
    console.log("我是函数外面的语句,我认识全局变量a值为:" + a);


    2.1.2局部变量(局部作用域)

    局部变量:和全局作用域相反,局部作用域一般只在固定的代码片段内可访问,而对于函数外部是无法访问的。

    例如:变量定义在function里面,这个变量就是局部变量,只在当前这个function函数内部能使用。在函数外部不能使用这个变量,出了这个function,就如同没有定义过一样。

    function fn(){
       var a = 100; //定义在函数的变量,局部变量
       console.log("我是函数里面的语句,我认识变量a值为:" + a);
    }
    fn();
    console.log("我是函数外面的语句,我认识变量a值为:" + a);

    avar在了function里面,所以现在这个a变量只能在红框范围内有定义:

    ES5语法中,JavaScript变量作用域非常简单,能关住作用域的只有一个,就是:函数。

     

    【总结】:

    ● 定义在function里面的变量,叫做局部变量,只在function里面有定义,出了function没有定义的。

    ● 定义在全局范围内的,没写在任何function里面的,叫做全局变量,都认识。

    【原理】:

    全局变量在定义时,就会直接生成一个新的变量,在任何位置查找变量都有定义。

    局部变量定义在函数内部,函数如果不执行,相当于内部的代码没写,局部变量等于从未定义过,在函数执行时,会在函数作用域内部立即定义了一个变量,使用完之后,变量立即被销毁。所以在外部永远找不到局部变量定义。


    2.2作用域链

    作用域链:根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问。

    当遇见变量时,JS引擎会从其所在的作用域依次向外层查找,查找会在找到第一个匹配的标识符时停止。

    在私有作用域中出现了变量,首先看是否为私有的,如果是私有变量,那么就用私有的即可。如果不是私有变量,则往当前作用域的上级作用域查找,如果上级作用域也没有,则继续往上查找....一直找到window为止。

    //变量的作用域,就是它var的时候最内层的function
    function outer(){
       var a = 1; //a的作用域是outer
       inner();
       function inner(){
           var b = 2;  //b的作用域是inner
           console.log(a); //能输出1,a在本层没有定义,就往上找
           console.log(b); //能输出2
       }
    }
    outer();
    console.log(a); //报错,因为a的作用域是outer

    多层嵌套:如果有同名的变量,那么就会发生“遮蔽效应”:

    var a = 10; //全局变量
    function fn(){
       console.log(a); //undefined,提升声明了局部变量
       var a = 13;     //把外层的a变量遮蔽了,这函数内部看不见外层的a
       console.log(a); //输出13,变量在当前作用域寻找,找到a定义为13
    }
    fn();
    fn();
    fn();
    console.log(a); //10,变量在当前作用域寻找,找到全局a

    一个变量在使用的时候得几?就会在当前作用域去寻找它的定义,找不到,去上一层找,直到找到全局(window),如果全局也没有,就报错。这就是作用域链。

    题目:

    var a = 1;        //全局变量
    var b = 2;        //全局变量
    function outer(){
        var a = 3;        //遮蔽了外层的a,a局部变量
            function inner(){
                var b = 4;  //遮蔽了外层的b,b局部变量
                console.log(a);   //① 输出3,a现在在当前层找不到定义的,所以就上一层寻找
                console.log(b);   //② 输出4
        }
        inner();        //调用函数
        console.log(a);    //③ 输出3
        console.log(b); //④ 输出2 b现在在当前层找不到定义的,所以就上一层寻找
    }
    outer();        //执行函数,控制权交给了outer
    console.log(a);    // ⑤ 输出1
    console.log(b); // ⑥ 输出2

    2.3不写var就自动成为全局变量了

    需要注意,函数内部声明的时候,一定要用var命令,如果不用,实际上声明了一个全局变量。

     function fn(){

        a = 100;//这个a第一次赋值时,没有var,所以就自动在全局作用域var了一次

     }

     fn();

     console.log(a);//100

    这是JS的机理,如果遇见一个标识符,从来没有var过,并赋值了:

     a = 100;

    那么就会自动在全局作用域定义var a;


    2.4函数的形参变量,会默认定义为这个函数的局部变量

    var a = 0;
    var b = 0;
    function fn(a,b){
       a = 3;
       b = 4;
       console.log(a,b);
    }
    fn();
    console.log(a);
    console.log(b);

    a,b就是fn内部的局部变量,只能在当前function函数内部使用,出了fn就没有定义。


    2.5全局变量的作用

    在函数内部使用自己的变量,尽量定义为局部。

    全局变量有自己独特的用途:累加、传递。

    累加:函数没执行一次,都要求变量在原来基础上发生变化。

    功能1:通信,共同操作传递同一个变量

    两个函数同时操作一个变量,一个增加,一个减少,函数和函数通信。

    var num = 0; //全局变量
    function add(){
       num++;
    }
    function remove(){
       num--;
    }
    add();
    add();
    add();
    add();
    remove();
    remove();
    add();
    add();
    console.log(num); //4

    功能2:累加,重复调用函数的时候,不会重置

     var num = 0;
     function baoshu(){
        num++;
        console.log(num);
     }
     baoshu();//1
     baoshu();//2
     baoshu();//3
     
    
     

    如果num定义在baoshu里面,每次执行完函数,作用域就被销毁,所以里面变量都是全新的。

    function baoshu(){
        var num = 0;
        num++;
        console.log(num);
    }
    
    baoshu();    //1
    baoshu();    //1
    baoshu();    //1

     2.6函数定义也有作用域

    function outer(){
       var a = 10;
       function inner(){ //局部函数
           console.log("哈哈");
       }
    }
    outer();
    inner(); //报错,因为全局作用域下,没有inner函数的定义
    console.log(a);//报错

    公式:

    function 大(){
       function 小(){
     
       }
       小(); 可以运行
    }
    小(); //不能运行,因为小函数定义在大函数里面,离开大函数就没有作用域。

    三、闭包

    闭包有两个作用:

    1、可以读取自身函数外部的变量(沿着作用域链寻找)

    2、可以让这些外部变量始终保存在内存中

    3.1闭包

    推导过程:之前已经学习过,inner这个函数不能在outer外面调用,因为outer外面没有inner定义。

    当函数执行的时候,会形成一个新的私有作用域,来保护里面的私有变量不受外界干扰,我们把函数的这种保护机制--->"闭包"

    function fn(){
    
    }
    fn();
    
    function outer(){
       var a = 100;
       function inner(){
           console.log(a);
       }
    }
    outer();
    inner(); //在全局作用域调用inner,全局没有inner的定义,所以报错

    但是我们就想在全局作用域下,运行outer内部的inner,此时我们必须想一些奇奇怪怪的方法。

    有一个简单可行的方法,就是让outer自己returninner

    非常经典的闭包案例,任何培训机构、书、讲闭包,一定是下面的案例:

    function outer(){
       var a = 888;
       function inner(){
           console.log(a); //888
       }
       return inner; //outer返回了inner的引用
    }
    var inn = outer();//inn就是inner函数了
    inn(); //执行inn,全局作用域下没有a的定义,但是函数闭包,能够把定义函数时的作用域一起记忆住,输出888

    一个函数可以把自己内部的语句,和自己声明时,所处的作用域一起封装成了一个密闭的环境,就叫“闭包”。

     

    每个函数都是闭包,每个函数天生都能够记忆自己定义时所处的作用域环境。但是,我们必须将这个函数,挪到别的作用域,才能更好的观察闭包。这样才能实验它有没有把作用域给记住

    我们发现,把一个函数从它定义的那个作用域,挪走,运行。嘿,这个函数居然能够记忆住定义时的那个作用域。不管函数走到哪里,定义时的作用域就带到了哪里。这就是闭包。

    闭包在工作中是一个用来防止产生隐患的事情,而不是加以利用的性质。

    因为我们总喜欢在函数定义的环境中运行函数。从来不会把函数往外挪。那为啥学习闭包,防止一些隐患,面试绝对考。

    使用全局变量接收,返回函数:

    var inn; //全局变量
    function outer(){
        var a = 250;
        var b = 500;
        //全局变量inn此时被赋值了一个函数
        //这个函数此时将立即生成闭包,记忆住此时所处的环境
        inn = function inner(){
            console.log(a);
            console.log(b);
        }
    }
    outer();
    var a = 300;
    var b = 400;
    inn();//一个函数在执行时,找闭包里面的变量,不会理会当前作用域

    闭包题目1

    function outer(x){
       function inner(y){
           console.log(x+y)
       }
       return inner;
    }
    var inn = outer(3);//接收到了inner函数,inn就是inner
    inn(5); //8
    inn(7); //10
    inn(10); //13

    闭包题目2

    function fun1(x,y){
       function fun2(x){
           console.log(x + y);
       }
       return fun2;
    }
    var f = fun1(3,4); //f就代表fun2
    f();  //NaN
    f(6); //10

    一般情况下:当函数执行会形成一个私有的作用域(形参赋值→预解析→代码执行),当这三步都进行完成后,浏览器会刚刚开辟的这个私有作用域进行回收,也就是说,函数执行完成,作用域立即销毁。


    3.2闭包的性质

    每次重新接收引用的函数时,闭包都是全新。

    function outer(){
       var count = 0;
       function inner(){
           count++;
           console.log(count);
       }
       return inner;
    }
    var inn1 = outer();
    var inn2 = outer(); //两个变量引用的是同一个inner函数,实际上两个引用变量中,都是全新闭包
    inn1(); //1
    inn1(); //2
    inn1(); //3
    inn1(); //4
    inn2(); //1
    inn2(); //2
    inn1(); //5
    inn1(); //6
    inn1(); //7

    无论它在何处被调用,它总是能访问定义时所处作用域中的全局变量。

    每个新的函数,不管通过何种结构生成,闭包都是新的,作用域也是新的,语句也是新的。通过同一个结构组成两个不同的函数,直接不会互相影响。

    实际应用:要考虑闭包对程序造成影响,了解原因,平时不会写个特殊结构。


    3.3作用域销毁的问题

    在函数中,return后面返回的值如果是一个函数,这个函数是不参与预解释的;函数体中return后面的代码也不执行,但是需要把后面的代码参预解释;

     

    销毁的作用域:一般情况下,函数执行完成后,当前的作用域都立即销毁;

    不销毁的作用域:

    当函数执行时,在私有作用域中返回了一个引用数据类型的值(例如:函数、对象、数组...)

    并且在函数外面,有变量接收了这个返回值,此时当前的这个私有作用域就被占用了,这个作用域也不能销毁了;

    作用域不销毁,里面的私有变量也不再销毁了。

    不立即销毁的作用域:

    当函数执行时,在私有作用域中返回了一个引用数据类型的值(例如:函数、对象、数组...)

    但是并没有变量在函数的外面接收,那么浏览器暂时先不销毁,等到浏览器空闲的时候,会自己销毁这个作用域。


  • 相关阅读:
    条件类的设计
    条件对象的设计
    又是一个星期天,明天又要开始一周的工作了,想想上周的工作情况,不怎么理想。
    自动设置的类,版本2,在设计上比前一个版本有进步。
    最近写了一个自动保存设置的类。
    关于异常信息"未找到成员"
    表达式类的设计
    IExtenderProvider 接口的应用.实现自定义组件LilyValidateProvider
    IIS404的问题
    程序开发[对象的旅行]
  • 原文地址:https://www.cnblogs.com/Javastudy-note/p/13812618.html
Copyright © 2011-2022 走看看