6-作用域链中变量的使用原则 闭包 回调/惰性/即时函数

作用域链中变量的使用原则

  • 在作用域链中使用(读取/设置)变量的时候, 首先在当前作用域链中查找, 找到就直接使用
  • 如果没有找到, 就去上一级作用域链中查找, 直到0级作用域

闭包

  • 一般情况下作用域: 内层作用域可以访问外层作用域, 反之不行

      function f1(){
         var n=999;
         function f2(){  
            alert(n); // 999
          }
        }
    
  • 有时候需要从外层作用域访问内层作用域

    1. 直接return, 一次性获取数据, 每次获取的不是同一份数据(变量创建后使用后就销毁了)

          function f1(){
             var n=999;
             function f2(){
               alert(n);
             }
             return f2;
          }
         var result=f1();
         result(); // 999
      
    2. 闭包技术: 可以间接访问封闭空间私有数据的方法

      • 闭包就是对直接return的数据进行包装(函数)
  • 闭包获取数据和设置数据

    • 判断不传形参的情况, 形参和变量不能同名 (同名的话, 形参在调用赋值时, 会先从当前作用域找同名变量)

闭包的好处

  • 获取数据只能通过指定的方法(接口)
  • 在设置数据的时候更加安全, 可以做一些校验工作
  • 延长变量的生命周期

setTimeout和闭包的执行

//第一种写法
for(var i=0; i<3; i++){
    (function(index){
        setTimeout(function(){
            console.log(index+'+++');
        },0);
    })(i);
}
//第二种写法
for(var i=0; i<3; i++){
    setTimeout((function(index){
        return function(){
            console.log(index+'----');
        }
    })(i),0);
}
//第三种方法
for(let i=0; i<3; i++){
    setTimeout(function(){            
        console.log(i+'----');            
    },1000);
}

div事件和闭包

  • JS的任务

    1. 渲染任务
    2. 代码的主要任务
    3. 事件性的任务(点击,定时器..)
  • JS是单线程

    1. 进程: 正在运行的应用程序 (工厂)
    2. 线程: 进程中用来执行任务的(工人),一个线程同一时间只能执行一个任务
    3. 串行执行: 多个任务一个一个的按顺序执行
    4. 并发执行: 多个任务同时执行
    5. 多程线:多条线程
    6. 多线程的原理: 1s = 1万个0.0001s cpu在多个任务之间来回的快速切换,造成多个任务同时执行的假象
    //JS单线程 先循环完 再触发点击事件时 i遍历完自加1变成5了--所以当点击div时变成5了
    for (var i = 0; i < divs.length; i++) {
        divs[i].onclick = function () {
            console.log('点击了第' + i + '个div');
        }
    }
    //方法一:闭包解决拿到i的值
    for (var i = 0; i < divs.length; i++) {    
        (function (j) {
            divs[j].onclick = function () {
                console.log('点击了第' + j+ '个div');
            }
        })(i);    
    }
    //方法二
    for (var i = 0; i < divs.length; i++) {
        divs[i].onclick = (function (j) {
            return function () {
                console.log('点击了第' + j + '个div');
            }
        })(i);
    }

函数的特殊性

  • 特殊性(特点): 函数本身是对象, 且对象可以提供作用域
    • 函数可以在运行时动态的创建, 还可以在程序执行过程中创建
    • 函数可以赋值给变量, 可以被扩展, 甚至删除
    • 函数可以作为其他函数的参数/返回值
    • 函数可以拥有自己的属性和方法
  • 注意
    • {}块在JS中不会创建作用域, 哪怕是if或者是while语句中使用var关键字申明的变量也并非局部变量, 函数是可以通过()调用并执行对象
  • 函数是第一类型对象
    • 函数可以向普通对象一样, 作为函数的参数 | 赋值给变量(函数表达式) | 作为函数的返回值返回

    • fun.name函数名可以获取, 但不能修改

        //函数作为参数传递
        setTimeout(function () {
            console.log(1);
        },100);
      
        //函数作为返回值
        function func() {
            return function () {
                console.log("demo");
            }
        }       
        var f = func();
        f();                //demo
      
        //函数赋值给变量
        var a = function(){}
        a();    //直接通过变量的名称调用函数
      

回调函数

  • 定义: 把一个函数作为其他函数的参数
    • 把函数作为另一个函数的参数
    //01 提供一个对象,该对象中永远showName方法
    var obj = {
        name:"默认的名字",
        age:30,
        showName:function () {
            console.log(this.name);
        },
        showAge:function () {
            console.log(this.age);
        }
    };                          

    //02 提供一个函数,该函数接受一个参数(函数引用)
    function demo(callBack,callBack_obj) {
        //处理第一个参数传递对象方法字符串的形式
        if(typeof callBack == 'string') {
            callBack = callBack_obj[callBack];
        }

        if (typeof callBack == 'function') {
            callBack.call(callBack_obj);
        }
    }

    //demo(obj.showName,obj);
    //demo(obj.showAge,obj);

    //传递字符串和对象来进行调用
    demo("showName",obj);

    //(01)以上代码传入两个参数,分别为具体的回调函数,和该回调函数所属的对象
    //(02)该函数的参数接受两种方式的回调传递(一种是直接传递函数引用,一种是直接以字符串的方式传递对象方法的字符串)
    //(03)在函数内部对传入的回调参数做处理,修正this的问题
  • 函数作为返回值
    • 使用闭包实现一个计数器(在该示例中setup函数的返回值为一个函数)
    • 通过调用返回值(一个函数),可以操作setup函数中的变量
    <script>
        var setup = function () {
            var count = 0;
            return function () {
                return count ++;
            }
        }
    
        var next = setup();
        console.log(next());    //0
        console.log(next());    //1
        console.log(next());    //2
    </script>

惰性函数

  • 定义: 函数真正的内容需要执行一次函数才能确定, 实现自我更新

          function foo() {
              console.log("foo!");
              //函数是引用型数据, foo的存储地址被重新定义了
              foo = function () { 
                  console.log("new foo!");
              }
          }
      
          //函数的调用
          //foo();  //foo!
          //foo();  //new foo!
    
  • 应用场: 函数有一些初始化的准备工作要做,且只需要执行一次的情况。

  • 注意点

    • 在函数上添加的属性或方法, 自我更新后无法访问
    • 把惰性函数赋值给变量, 以变量的(对象的方法)方式来调用, 不会更新(调用仍然是同一份数据)


      惰性函数以对象的形式访问.png

      惰性函数赋值给变量.png

即时函数

  • 定义: 在函数定义之后立即执行该函数。

           //第一种写法
           (function () {
               console.log("即时函数的第一种写法");
           })();
    
           //第二种写法
           ;(function () {
                console.log("即时函数的第二种写法");
           })();
          
           //补充写法
           (function (a) {
                console.log(a);
           }(20));
    
           +function (b) {
                 console.log(b);
           }(30);
    
           -function (b) {
                console.log(b);
           }(40);
    
  • 模式组成

    • 使用函数表达式来定义函数(匿名函数, 不能使用函数声明方式)
    • 在函数表达式末尾添加一组(), 表示立即执行当前函数
    • 将整个函数包装在()中, 有两种方式
  • 作用

    • 用来将所有的代码包装到当前的作用域中,并且不会将任何的变量泄露到全局作用域中。
    • js中没有代码块作用域,而函数是js中唯一可以创建作用域的。
    • 即时函数就是利用了函数创建作用域这一点,来实现对一些需要封装且不允许外部访问的操作。
  • 优点

    • 不会产生全局变量,在即时函数内部定义的所有变量都仅仅只是该函数的局部变量,不会造成全局变量污染问题。
    • 具有更好的封装性,外部无法访问到该函数内部的数据。
  • 即时函数的传参和返回值

       //01 接受参数
       (function (str) {
           console.log(str);           //hello
       })("hello");
    
       //02 提供返回值并赋值给新的变量
       var foo = (function () {
          return 2 + 1;
        })();
    
       console.log(foo);           //3
    

即时对象初始化

  • 结构特征
    • 提供一个对象, 在该对象内部提供一个init初始化方法
    • 使用()把对象包装起来(让字面量变成表达式)
    • 然后随即调用init方法, 完成初始化操作
  • 基本结构
    • 即时对象初始化: ({init: function(){}}).init();
  • 模式优点
    • 在执行一次性的初始化任务时保护全局的命名空间
({
    name:"张三",
    age:23,
    getDescript:function () {
    console.log("名字:" + this.name + "年龄:" + this.age);
},

//注意:在对象中访问对象的属性和方法都需要使用this.前缀
init:function () {
    this.getDescript();
    //其他的初始化处理
}
}).init();

设计模式: 为了解决开发中遇到的一类问题而提出的一套方法

  • 要求: 一般一套系统需要设计模式
  • 来源: 建筑行业
  • 工厂模式核心
    • 提供一个父构造函数-->开了一家工厂
    • 设置父构造函数的原型对象-->产品公共东西
    • 在父构造函数上提供一个静态的工厂方法-->生产产品
      1. 接受传入的参数
      2. 判断是否支持生产
      3. 设置子构造函数的原型对象是父构造函数的一个实例(原型链继承)
      4. 利用子构造函数创建对象并且返回
    • 定制合作伙伴
    • 利用父构造函数的静态工厂方法创建对象
  • 作用
    • 通过统一的方法/接口创建对象, 并且根据不同的类型创建不同的对象, 便于代码的维护和扩展
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容