Function、闭包

1.函数

1.函数声明
    function 函数名(参数列表){函数体;return返回值}
注意:函数里的变量会声明提前
2.赋值
    let 函数名=function(参数列表){函数体;return返回值}
优点:函数里的变量不会声明提前
3.用new
    let 函数名=new Function("参数1","参数2","…","函数体";)
此方法不推荐使用

2.重载

(1)定义:相同函数名,不同参数列表的多个函数,在调用时,可根据传入参数的不同,自动选择对应的函数执行

(2)优点:减少API的数量,减轻调用者的负担

(3)语法:js语法默认不支持重载,因为 js不允许多个同名函数同时存在,最后创建的会覆盖之前创建的所有

  解决:arguments

arguments:
    函数调用时自动创建的 接收所有传入函数的参数值的 类数组对象
类数组对象:长的像数组的对象,跟数组比较,类型不同,API不通用

练习1:使用重载,定义一个函数,当不输入数字是在控制台输出“不合格”,当输入一个数字时输出“合格”,当输入两个数字时输出“优秀”

        function pay(){
            switch(arguments.length){
                case 0:console.log("手机支付");
                    break;
                case 1:console.log("现金支付");
                    break;
                case 2:console.log("刷卡支付");
                    break;
            }
        }
        pay();
        pay(100);
        pay("123456","654321");

练习2:利用函数的重载,计算一串数字的和

        function add(){
            let sum=0;
            for(let i=0;i<arguments.length;i++){
                sum+=arguments[i];
            }
            return sum;
        }
        console.log(add(1,2,3));

3.匿名函数

1.优点:节约内存、划分临时作用域
2.使用场景:一个函数使用后,希望立即释放的
3.回调(callback):将一个函数,交给另一个函数去自动调用
    比如:arr.sort(function(a,b){return a-b;})
            xhr.onreadystatechange=function(resText){}
            btn.onclick=function(){}
            str.replace(/正则/,function(kw){return xxx})
4.自调:定义函数后,立即调用自己
    使用场景:几乎所有自定义的脚本,都要放在匿名函数中
    优点:避免使用全局变量,避免全局污染
    语法: (function(…){…})()

4.作用域(scope)和作用域链

(1)用途:一个变量的可用范围,其实是一个保存变量的对象,为了避免不同范围的变量间互相干扰

(2)包括

1. 全局作用域: window
    保存全局变量: 优: 可重复使用,随处可用
                        缺: 全局污染
2. 函数作用域: 临时创建的活动对象AO
    保存局部变量: 优: 不污染全局
                        缺: 不可重用,仅函数内可用

(3)原理

1. 程序开始执行前: 
    在内存中创建执行环境栈(数组): 
    执行环境栈: 依次保存正在调用的函数的数组
         函数调用后,会出栈
    在栈中首先添加浏览器主函数的调用
    创建全局作用域对象window
    主函数引用全局作用域对象window

2. 定义函数时: 
       在window中用函数名创建变量
       在window外创建函数对象保存函数的内容
       函数名变量通过地址引用函数对象
       函数对象用隐藏的scope属性,引用回自己诞生的作用域对象。——万一运行时缺东西,可以找诞生的作用域要!

 

3. 函数调用时: 
       在执行环境栈中添加当前函数调用
       为本次函数调用创建活动对象AO:
         活动对象: 专门保存本次函数调用所需的局部变量的对象——就是函数作用域对象
       在活动对象中保存函数的局部变量
       活动对象通过隐藏的parent属性引用window
       变量的使用顺序: 先用AO中的局部变量,局部没有,才去window找。

4. 函数调用后: 
       函数的调用从ECS中出栈
         导致: 函数作用域对象AO释放
           导致: 局部变量一同释放!

 

(4)作用域链: 由多级作用域对象,逐级引用形成的链式结构

    作用: 存储所有变量(局部变量和全局变量)

          控制着变量的使用顺序,先局部再全局

 5.闭包

1.作用:重用一个变量,又保护变量不被篡改的一种机制
2. 全局变量:优点:可重用
                  缺点:造成全局污染
    局部变量:优点:仅函数内可用,不会污染全局,也不会被篡改
                  缺点:不可重用
3.闭包形成原因:外层函数的函数作用域对象无法释放
4.过程
    1.用外层函数包裹,要保护的变量和使用变量的内层函数
    2.外层函数返回内层函数
    3.调用者,调用外层函数,获得内层函数对象
缺点:闭包比普通函数占用更多内存
解决:用完后立刻释放
5.注意:
  1. 先找受保护的变量和内层函数,画简图:
  2. 同一次外层函数调用,返回的多个内层函数,共享同一个闭包变量
  3. 多次外层函数调用,会创建多个闭包。多个闭包之间彼此毫无关系!

练习1:在控制台从1开始打印

        //闭包
        function outer(){
            let i=1;
            return function(){
                console.log(i++);
            }
        }
        let getNum=outer();
        //getNum:function(){ console.log(i++); }
        getNum();
        getNum();
        getNum();
        getNum();
        getNum();
        getNum();
        //释放闭包
        getNum=null;

练习2:在页面中设置一个按钮显示“赞+0”,每次点击一次按钮,赞就+1

    <button id=btnZan>赞(+0)</button>
    <script>
        (function () {
            let n = 0;
            btnZan.onclick = function () {
                btnZan.innerHTML = "赞(+" + (++n) + ")";
            }
        })()
    </script>

练习3:在页面设置一个点赞按钮和一个点踩按钮,每次点击都会+1

    <button id=btnZan>赞(+0)</button>
    <button id=btnCai>踩(+0)</button>
    <script>
        function outer(id,tite) {
            let n = 0;
            return function () {
                id.innerHTML = tite+"(+" + (++n) + ")";
            }
        }
        btnZan.onclick=outer(btnZan,"赞");
        btnCai.onclick=outer(btnCai,"踩");
    </script>

 

posted @ 2020-11-16 21:18  you_rgb  阅读(235)  评论(0)    收藏  举报
//一下两个链接最好自己保存下来,再上传到自己的博客园的“文件”选项中