函数&作用域&闭包

函数 function

- 函数也是一个对象,普通对象能做的函数都能做,在函数中可以保存可执行的JS代码,并且在需要的时候对这些代码进行调用
- 创建函数的方式一:
            函数声明:

                function 函数名(形参1,形参2,...形参N)
{
                    语句...
                }
  例如:
    function fun()
{
            console.log("我是fun中封装的代码");
    }
- 创建函数的方式二:
    函数表达式:
            语法:
var 变量  = function(形参1,形参2,...形参N)
{
            语句...
        }
例如:
    var fun2 = function()
{
    console.log("fun2中的代码~~~");
}
  • 调用函数,将函数中存储的代码执行
  • 调用函数:函数对象()
    fun();
  • 在声明函数的时候,可以在函数的()中指定数量不等的形参,形参(形式参数),声明形参就相当于在函数内部声明了变量但是并不赋值,若没有赋值,默认的就是undefined.
  • 调用函数时,可以在()传递实参(实际参数),实参会赋值给对应的形参 (总结:在声明时是形参,在调用时是实参)
    • 形参和实参对应的个数问题:
      • 调用函数时,JS解析器不会检查实参的个数和类型,可以传递任意数量的实参
      • 如果形参和实参数量一样,则对应的实参会赋值给对应形参
      • 如果实参的数量大于形参,则多余的实参不会使用
      • 如果实参的数量小于形参,则没有对应实参的形参值会是undefined
    • 可以传递任意类型的实参,函数可以接收任意类型的实参,也可以是一个对象,函数也可以作为一个函数的参数
      调用 fun(sum); 这里sum也是一个函数。
      将一个匿名函数作为参数传递fun( function()
      {
      alert(“hello我是参数!!!”);
      });
      -返回值return;(只使用在函数当中)
  • 要使用返回的结果,就必须使用一个变量来保存返回值
    • 所谓的返回值就是指函数执行完毕以后返回的结果
      • 通过return来设置函数的返回值
        语法: return 值;
      • 如果不指定返回值,或return后不跟任何值,则会返回undefined
      • 调用函数时,可以通过一个变量来接收函数的返回值。返回值自己不会输出,只是作为值返回来,接收到后再用输出语句就可以输出。
      • 语法: var 变量名=函数名();
        例如: var result = sum(345,234,312);
        console.log(result);
      • 函数中,return一旦执行,函数立即结束,return后的代码都不会在执行了
      • break用于结束当前循环
      • continue用于跳过当次循环
      • return可以用来退出函数
  • console.log(fun); //打印函数体
    console.log(fun()); //打印函数的返回值
  • 函数的返回值可以是任意的数据类型
  • retrun 值 ,若值是数字/字符串/布尔类型/undefined/null,则可以直接加在后面,值会返回来;若是字母需要定义后才能使用。它的值不能是语句。
  • 在函数当中嵌套有if语句,这时if语句中,就可以使用return来阻止他后面的语句。这时if后面的语句都会被阻止,跟if同级的语句也会被阻止(在一个函数中,只要见到return的地方,他后面的语句都不会再执行了)!!!

函数和循环语句的作用的区别:
 函数:是否重复使用,取决于你调用的地方。
 循环体:只用当执行到当前语句时,才能执行多次。

  • 预定义函数:
    eavl() :可以执行‘字符串’类型的JS代码,但是他的安全性不高。
    例如:var str=“console.log(‘this is string’)”; //字符串
    eval(str); //输出结果为 this is string
    对中文进行编码和解码:
    encodeURI():对字符串进行编码
    decodeURI():对字符串进行解码

  • 按值传递:就是全局变量和局部变量同名时的值传递。

  • 定义函数时,变量名和函数名同名时的情况:

  • 使用字面量定义函数的情况:
    第一函数的结构——其实就是使用了变量的声明方式(将一个函数赋给变量)
    特点:和变量的特点是一样的
    例如: var fn=‘string’;
    var fn=function(){ //相当于重新的赋值了
    console.log(‘this is string’);
    }
    console.log(fn()); //this is string
  • 函数声明的方式:
    直接被定义为了一个函数——不是变量
    当定义的变量和声明的函数是相同的名字时,变量的名字没有被覆盖,变量还是变量,没有做变化。此时,“变量有效,函数无效”,定义的变量还可以输出,但若输出函数就会报错。
  • 变量的声明提升,会提升到函数定义之前。
    例如:var fn=‘string’;
    function fn(){
    console.log(‘this is string’);
    }
    console.log(fn); // string
    console.log(fn()); //fn is not a function
    因为,当变量声明和函数声明同名时,变量有效,函数无效,就是函数不可用。

  • 作为值的函数:

  • 为什么要使用特殊函数:来实现代码的优化 ,代码量、性能、可读性等等。

  • 匿名函数(不能单独使用,会语法报错)
    • 自调函数:自己调用自己,将调用函数过程给省略了
      语法结构:第一个小括号 —— 定义函数
      第二个小括号 —— 调用函数(一般将其省略,若需要传递实参,则括号里面添加实参)
      语法结构:(function(形参){
      //函数作用域 – 不占用全局的命名空间
      函数体…
      })(实参);
      其他语法结构: ( function(形参){函数体}(实参) )
  • 特点:仅仅只能被调用一次,使用的好处是不占用全局的命名空间,优化代码性能。
  • 回调函数:将一个函数作为另一个函数的参数,作为参数的函数就叫做回调函数。
    作为回调函数:1,回调函数的调用体,在外层函数的定义体内;
    2,回调函数的定义体,在外层函数的调用体内。

    例如: var one=function(){ one(),two(),就是回调函数
    return 1;
    }
    var two=function(){
    return 2;
    }
    function fn(a,b){
    return a() +b(); 回调函数,传的是实参
    }
    console.log(fn( one,two ));
    输出语句也可以改写成:
    console.log(function(){return 1;},function()){return 2;} //匿名回调函
    分别是a() b()的回调函数的定义体

作用域(scope)

  • 作用域指的就是作用范围
  • 在JS一共有两种作用域
    全局作用域
    函数作用域
  • 全局作用域
  • 所有直接写在script标签中的内容都在全局作用域中
  • 生命周期:全局作用域在页面加载时创建,在页面关闭时销毁
  • 在全局作用域中有一个全局对象window,该对象由浏览器创建,代表的是整个的浏览器的窗口。在全局作用域中创建的变量都会作为window的属性保存,在全局作用域中创建的函数都会作为window的方法保存
  • 全局作用域中创建的变量都是全局变量,可以在页面的任意位置访问
  • 声明变量时,如果不写var关键字,就相当于直接向window对象中添加属性
  • 函数作用域
    • 函数作用域在调用函数时创建,在调用结束时销毁,每调用一次函数就会创建一个新的函数作用域
  • 在函数作用域中,可以访问全局作用域的变量;在全局作用域中,无法访问到函数中的变量
  • 在函数作用域中如果不使用var关键字,则变量会变成全局变量
  • 所有使用var关键字声明的变量,会在函数中的其他代码执行前被声明并没有赋值,所有使用函数声明创建的函数,会在函数中的其他代码执行前被创建。
  • 作用域链:在函数的嵌套中使用一个变量时,它会先在自身的作用域中寻找,如果找到了则直接使用。如果没有找到则去上一级函数作用域中寻找,直到找到全局作用域为止,如果全局作用域中依然没有,则会报错。全局作用域不能访问函数作用域中的变量。

  • 变量的提升(变量的声明提前,提升到最上面一行) 只针对变量声明和函数声明有用。

    • 在JS中所有使用var关键字声明的变量,都会在所有的代码执行之前被声明但是不会赋值,但是赋值会等到赋值语句执行时才进行。
    • 如果不使用var关键字不会具有声明提前的特性。
  • 函数的提升(函数的声明提前,提升到函数体里面的最上面一行)
    • 在JS中,使用函数声明创建的函数会在所有的代码执行之前被创建,所以我们可以在函数声明之前就去调用函数
    • 使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用
    • 在ECMAScript 5 中定义的‘语句’中的变量,是全局变量,在任意位置都可以访问被访问。
  • 函数声明的提升,并不是undefined ,而是函数定义体。
    例如: console.log(fn); //得到的是fn函数的定义体
    function fn(){
    console.log(‘this is fn’);
    }

    • 如果一个变量和一个函数已经声明过了,并且在下面又进行了一次声明,则变量不会做改变(除非是进行声明和赋值才会做改变),但是函数会变成最后声明的那个函数(后面声明的函数会将前面声明的函数覆盖)。在全局作用域中,函数会提升在变量之前。先提升函数再提升变量。
    • 变量的提升不会搭理if条件的暗示。
    • 最佳实践:在块内部不要去定义函数。
      *注意:函数的作用域在其创建时就已经确定了,无论在何处调用都不会改变它的作用域
      例如:
  • 函数执行的流程

  • 栈 是一种数据结构和数组很类似
  • 栈的特点 后进先出
    1. 创建一个新的执行环境,并且将其放入到执行环境栈的栈顶
      • 执行环境是放到一个栈结构中保存的,栈顶保存总是当前的环境,栈底总是全局的环境
    2. 创建一个新的变量对象,并且将变量对象放入到作用域栈的栈顶
      • 变量对象是用来保存变量的对象,函数执行时所有的局部变量和函数
        都会保存到变量对象中,变量对象无法访问。除了全局的变量对象window
    3. 预解析,将使用var声明的变量添加到变量对象中,将函数声明创建函数创建好并添加到变量对象中
    4. 逐行执行函数中的代码
    5. 函数执行完毕将变量对象从作用域栈中弹出并销毁,将环境从环境栈中弹出并销毁。

闭包(难点)

  • 概念:函数可以访问函数之外定义的变量
  • 闭包结构必须满足的条件:
    1,具有两个关系是平行的函数作用域
    2,其中一个函数作用域中具有局部变量
    3,在另一个函数作用域中可以访问这个函数作用域的局部变量
    我们常见的闭包结构:作为值的函数 和 回调函数
  • 闭包在创建时,每次都是创建一个新的闭包,每个闭包都是新的个体。
    图解:
    例如: //定义两个平行关系的函数作用域
    function fn1(){
    //在这个函数作用域中定义一个局部变量
    var a=‘a’;
    fn2(a); //调用fn2(),将a的值作为实参传递给fn2()函数中的形参b,则fn2可以得到变量a的值。
    }
    function fn2(b){
    console.log(b) //fn2()可以得到a的值
    }
    fn1();//是有限制的,必须先执行fn1()函数
    fn2();

  • 作为值的函数:将一个函数作为另一个函数的结果进行返回,作为结果返回的函数称之为作为值的函数。(在一个函数的return 后编写一个函数)

  • 作为值的函数的闭包
    function fn(){
    var a=‘a’;
    return function(){
    return a; //a
    }
    }
    var fun=fn();
    console.log(fun());

解析:定义一个返回值为函数的函数fn,调用fn后,得到值的返回函数function(){return a;},并将其赋值给fun.得到的结果为fun=function(){return a;},在调用fun,就能最后的输出结果为‘a’

  • 作为回调函数的闭包
    function fn(n){
    var a=‘a’;
    return n(a); //调用,实参
    }
    var fun=fn( function(c){return c;} );
    console.log(fun);
    “`
    解析:调用fn(),它的结果是什么,是由fn()的返回值来决定的。这里它的返回值是调用函数n(),传递的是实参;它的函数定位为function(c){return c;},将‘a’传入后,得出fun=‘a’,他是一个值,所以另一个全局函数是function(c){return c;}

  • 闭包的问题:
    将局部变量的生命周期无限延长,将导致浏览器内存溢出。
    回调函数都是闭包结构,所以有“回调陷阱”问题。将创建的闭包销毁
    解决方案:
    最后手动释放资源,闭包函数名=null

  • 如何写一个闭包:
    1.函数嵌套
    2.内部函数访问外部函数的变量
    3.使用各种手段将内部函数传递出来
    4.调用外部函数

    例子:闭包的应用

  • 对数据可以写入和只读
    var getvalue,setvalue;
    (function(){
    //定义函数中的局部变量,之外的环境不能使用
    var content=0;
    //该方法只能读取当前局部变量的值
    getvalue=function(){
    return content;
    }
    //该方法可以修改当前局部变量的值
    setvalue=function(x){
    content=x;
    }
    })()
    setvalue(100);
    console.log(getvlaue());
    解析:首先定义了两个变量,()()自调函数从进入JS代码只能使用一次,在自调函数中重新将两个变量定义为函数,所以当自调函数执行完之后,这两个变量成为全局函数,可以进行多次的调用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值