【JS高级】js之闭包对象_04

目录

❣️ 闭包

1. 存在问题

2. 如何解决

3. 什么是闭包

4. 何时使用闭包

5. 如何使用闭包

6. 示例:使用闭包

7. 闭包的原理

8. 一句话闭包是如何形成的

9. 闭包的缺点

💥 扩展:this判断—8种指向

💠 总结:知识点提炼 


🆙【前文回顾】👉  js之函数、重载、匿名函数、作用域及作用域链_03


  

❣️ 闭包

1. 存在问题

         全局变量和局部变量都有不可兼得的优缺点:

         (1). 全局变量:

         a. 优点: 可反复使用

         b. 缺点: 随处可用,极易被污染!

         (2). 局部变量:

         a. 优点: 仅函数内可用,不会被篡改!

         b. 缺点: 不可重用!

示例:全局变量

0_closure - 0.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>全局变量</title>
</head>
<body>
  <script>
    //需求: 定义一个函数,可以帮小孩儿管理零花钱
    //假设小孩儿有1000块钱零花钱
    var total=1000;//全局
      //每次传入花的钱数
    function pay(money){
        //从总价中减去花的钱数
      total-=money; //total前没有加var,为全局变量
        //输出余额
      console.log(`花了${money}, 还剩${total}`);
    }
    //第一次花了100
    pay(100);//花了100,还剩900

    //别人的程序中刚好也用到了total变量
    total=0;

    //第二次又花了100
    //造成-100的根源就是使用了全局变量total,途中被人从900篡改为0,即全局污染
    pay(100);//正常情况下,花了100,此时理应还剩800(第一次被调用后//全局变量的值变为了900——因 
    //为是全局变量,是不会被释放的。
   //注意:pay函数自身只有一个局部变量就是money),由于途中被人从//900篡改为0,所以,实际输出结 
   //果为还剩-100

  </script>
</body>
</html>

运行结果:

花了100, 还剩900
花了100, 还剩-100 

示例:局部变量

0_closure - 1.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>局部变量</title>
</head>
<body>
  <script>
    //需求: 定义一个函数,可以帮小孩儿管理零花钱
    //假设小孩儿有1000块钱零花钱
    // var total=1000;//全局
      //每次传入花的钱数
      function pay(money){
        var total=1000;//局部
        //从总价中减去花的钱数
        total-=money;
        //输出余额
        console.log(`花了${money}, 还剩${total}`);
      }
    //第一次花了100
    pay(100);//花了100,还剩900

    //(虽然此时)局部total=900
    //(但是)pay()调用后,局部变量释放(也就是说局部total=900会被释放,并不能存活下来)——请注意 
    //total是函数的局部变量,每次调用后是//会被释放的)

    //别人的程序中刚好也用到了total变量,但是这次并不会影响局部变量total的值
    total=0;//全局————无法访问局部

    //第二次又花了100
    //局部total=1000————第二次调用时局部total又等于了1000了
    //局部total-=100————是从局部total=1000减去100,此时仍为900
    //造成900的根源就是使用了局部变量total,因为局部变量仅函数作用域内可用且函数调用后,局部变量会被释放,此时的局部变量值不能存活下来,也就不能被反复使用了
    pay(100);//正常情况下,花了100,此时理应还剩800,实际输出结果为还剩900,从此,你拥有取之不尽的钱
  </script>
</body>
</html>

运行结果:

花了100, 还剩900
花了100, 还剩900

2. 如何解决

使用闭包!

3. 什么是闭包

         什么是闭包: 2个概念:

         (1). 用法: 即重用变量,又保护变量不被篡改的一种编程方式!

         (2). 本质: 外层函数的作用域对象,被内层函数的作用域链引用着,无法释放,形成了闭包!(闭包本质就是外层函数的作用域对象)

4. 何时使用闭包

今后只要希望一个函数即重用一个变量,又保护变量不被篡改时,都用闭包

5. 如何使用闭包

         如何使用闭包: 3步:

         (1). 用一个外层函数,包裹要保护的变量和使用变量的内层函数

         (2). 外层函数将内层函数返回到外部,供外部使用

         (3). 外部的使用者,要调用外层函数,获得return出来的内层函数对象,保存在变量中,反复使用!

6. 示例:使用闭包

⏬ 使用闭包为小孩儿保管零花钱 ⏬

1_closure.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    //需求: 定义一个函数,可以帮小孩儿管理零花钱
    //1. 用一个外层函数,包裹要保护的变量和使用变量的内层函数
    function parent(){//父母
      //假设小孩儿有1000块钱零花钱
      var total=1000;//放在父母函数内,内层函数外,由父母替内层函数代为保管!
      //每次传入花的钱数
      //2. 外层函数将内层函数返回到外部,供外部使用
      return function(money){//局部函数
        //从总价中减去花的钱数
        total-=money;
        //输出余额
        console.log(`花了${money}, 还剩${total}`);
      }
    }
    //3. 外部的使用者,要调用外层函数,获得return出来的内层函数对象,保存在变量中,反复使用!
    var pay=parent();
    //pay: function(money){
      //total-=money
      //console.log(... ...);
    //}

    //第一次花了100
    pay(100);//花了100,还剩900
    //局部total=900
    //pay()调用后,局部变量释放
    //别人的程序中刚好也用到了total变量
    total=0;

    //第二次又花了100
    //局部total=1000
    //局部total-=100
    pay(100);//花了100,还剩800
  </script>
</body>
</html>

运行结果:

花了100, 还剩900

花了100, 还剩800 

7. 闭包的原理

         (1). 创建外层函数对象

         (2). 调用外层函数对象,获得return出来的内层函数对象:

注意:0x9091是外层函数parent的作用域对象的地址

         (3). 外层函数调用后:

         (4). 调用内层函数时:

注意:0x1000是内层函数的作用域对象的地址

         (5). 内层函数调用后:

         (6). 有人想篡改闭包中的变量:

         (7). 反复调用pay函数时

注意:0x2000是内层函数的作用域对象的地址

8. 一句话闭包是如何形成的

外层函数调用后,由于内层函数的作用域链引用着外层函数得作用域对象,无法释放,形成了闭包!即外层函数的作用域对象,被内层函数的作用域链引用着,无法释放,形成了闭包!

就是下面这张图,即第三张图就是大名鼎鼎的闭包对象,闭包对象就是被内层函数作用域链拽着(引用着)始终不放的、由妈妈创建的函数作用域对象,就叫做闭包对象🔽

9. 闭包的缺点

比普通的函数多占用了一块内存!(即多出了闭包对象占用的内存空间)

         解决: 当一个闭包结构不再使用时,尽量及时手工释放:

         应该将保存内层函数对象的变量赋值为null:

         比如: pay=null;

10.笔试面试时画简图: 找3样东西:

         (1). 外层函数——妈妈

         (2). 外层函数的局部变量——红包

         (3). 内层函数——孩子

补:内层函数之所以能访问使用外层函数的局部变量,是因为内层函数的作用域链引用着外层函数的作用于对象,因为pay的function来自于函数内部(调用后,返回值是内层函数function(money){},保存在变量pay里面,即var pay=parent()即var pay=function(money){ }),所以这个function的作用域链牢牢的拽着(引用着)外层函数的作用域对象,里面放着total=1000,所以可以访问外层函数的局部变量total。

 

💥 扩展:this判断—8种指向

this  8种指向: 判断this,一定不要看定义在哪儿!只看调用时!

1. obj.fun()   this->obj

2. fun() 或 (function(){ ... })() 或 多数回调函数 或 定时器函数   this->window

3. new Fun()   this->new正在创建的新对象

4. 类型名.prototype.共有方法=function(){ ... }   this->将来谁调用指谁,同第一种情况

5. DOM或jq中事件处理函数中的this->当前正在触发事件的DOM元素对象

                               如果需要使用简化版函数,必须$(this)

6. 箭头函数中的this->箭头函数外部作用域中的this

7. jQuery.fn.自定义函数=function(){ ... }   this->将来调用这个自定义函数的.前的jQuery子对象,不用再$(this)

8. new Vue()中methods中的函数中的this->当前new Vue()对象


💠 总结:知识点提炼 

1. 只要验证字符串格式或查找、屏蔽敏感词时都要用正则

(1). 最简单的正则: 一个敏感词的原文

(2). 某一位字符上可能有多种备选字时用: [备选字列表]

(3). 如果[]中部分字符是连续的,可用: [x-x]

         a. 一位小写字母: [a-z]

         b. 一位大写字母: [A-Z]

         c. 一位字母(大小写都行): [A-Za-z]

         d. 一位字母或数字都行: [0-9A-Za-z]

         e. 一位汉字: [\u4e00-\u9fa5]

(4). 预定义字符集:

         a.  \d 一位数字

         b.  \w 一位数字、字母或_

         c.  \s  空格、tab、换行等空字符

         d.  .   任意字符

(5). 如果规定一个字符集或子规则反复出现的次数时就用量词:

         a. 有明确数量边界的量词:

      1). {n}  =n 必须n个,不能多也不能少

      2). {n,m}  n个<=    <=m个

      3). {n,}    n个<=   多了不限

         b. 没有明确数量边界的量词:

      1). *   0个<= 可有可无,多了不限

      2). ?   0个或1个  可有可无,最多一个

      3). +   1个<=  至少一个,多个不限

(6). 两个规则中选其一匹配即可: 规则1|规则2

(7).希望将多个子规则分为一组先联合匹配,再和分组外的其他规则联合匹配:

  (多个子规则)

(8). 匹配特殊位置: 3个

         a. 字符串的开头位置: ^

         b. 字符串的结尾位置: $

         c. 英文句子中的单词的左右边界: \b

2. String家提供的正则相关函数: 3件事

(1). 查找敏感词: 4种情况

      a. 查找一个固定的敏感词出现的位置:
         var i=str.indexOf("敏感词")

         // 如果找不到,返回-1

      b. 用正则查找多种敏感词出现的位置:
         var i=str.search(/正则/i)

         // 如果找不到,返回-1

      c. 查找敏感词的内容:

      1). 查找第一个敏感词的内容和位置:
      var arr=str.match(/正则/i)

      // arr: [ 0:"敏感词内容", index:敏感词位置 ]

      // 如果找不到返回null

      2). 查找所有敏感词的内容,不关心位置:
      var arr=str.match(/正则/ig)

      // arr: [ 敏感词1, 敏感词2, ...  ]

      // 如果找不到返回null

     d. 查找每个敏感词的内容和位置: reg.exec

补: js中所有数组底层本质都是关联数组(下标都为字符串)

1. 访问数组中元素值的标注写法:

  arr["下标"]

2. 简写:

  a. 如果下标为自定义字符串名称,可简写为:

  arr.自定义名称的下标

  b. 如果下标为数字内容的字符串,可简写为:

  arr[数字下标]

总结: 查找方法的返回值规律

1. 如果原函数返回的是下标位置i,如果找不到,都返回-1

2. 如果原函数返回的是一个数组arr或一个对象obj,如果找不到,都返回null

3. 如果原函数返回类数组对象,如果找不到返回空类数组对象:

   { length:0 }

(2). 替换敏感词: 2种

         a. 简单替换:
         变量=str.replace(/正则/ig, "新值")

         b. 高级替换:
         变量=str.replace(/正则/ig, function(形参){

               return 根据本次敏感词动态生成一个新值

         })

         c. 删除敏感词:
         变量=str.replace(/正则/ig, "")

(3). 切割字符串:

         a. 简单切割:
         var arr=str.split("切割符")

         b. 复杂切割:
         var arr=str.split(/正则/i)

         c. 打散字符串为字符数组:
         var arr=str.split("")

3. RegExp对象: 

         (1). 创建正则表达式对象:

         a. 如果正则是固定的:
         var reg=/正则/ig

         b. 如果正则需要动态生成:
         var reg=new RegExp("正则",ig)

         (2). 验证字符串格式:
         var bool=reg.test(str)
         reg必须同时前加^后加$

         (3). 既查找每个关键词的内容又查找每个关键词的位置: (待续)

         do{

               var arr=reg.exec(str);

               if(arr!=null){

                              获得本次找到的敏感词的内容(arr[0])和位置(arr.index)

               }

         }while(arr!=null);

4. 函数: 

(1). 创建函数三种方式:

         a. function 函数名(形参列表){ 函数体; return 返回值 } //会被声明提前,不好

         b. var 函数名=function(形参列表){ 函数体; return 返回值 }//不会被声明提前,首选

         c. var 函数名=new Function("形参1", "形参2", ... , "函数体; return 返回值")

函数本质:

  1). 函数也是一个对象,对象中保存着函数的函数体代码

  2). 函数名只是一个普通的变量,函数名通过函数对象地址,引用着函数对象

  3). function在底层等效于new Function()

    function 函数名(){ ... }和var 函数名=function(){}在底层都会被翻译为

    var 函数名=new Function(...)

    只不过function 函数名(){}是先提前,再翻译

    而var 函数名=function(){}是不提前,原地翻译

(2). 重载: 今后,一件事,根据传入不同的参数值,动态执行不同的逻辑时,都用重载

function 一个函数名(不写形参变量){

  //arguments对象自动接住所有实参值

  if(arguments.length==0){

    执行一种逻辑

  }else if(arguments.length==1){

    执行另一种逻辑

  }else{

    执行其它逻辑

  }

}

其中arguments是类数组对象: 和数组相比:

         a. 相同点: 也有下标,length属性,也可for循环遍历

         b. 不同点: 不是数组类型,无法使用数组家的函数

(3). 匿名函数:

         a. 所有回调函数优先使用匿名函数——用完释放,节约内存

         b. 所有js代码都应该保存在匿名函数自调中,禁止使用全局变量,避免全局污染!

         (function(){

               要执行的js代码

         })();

         结果: 匿名函数内的都是局部变量,不会产生全局变量。

         局部变量随匿名函数一起释放。不会污染全局。

(4). 作用域和作用域链: (跟着视频亲自画图!!!)

         a. 作用域:

         1). 全局作用域:window,保存全局变量

         优: 可重用,缺: 随处可用, 极易被污染

         2). 函数作用域: 保存局部变量

         局部变量包括2中: 函数中var出的变量和形参变量

         优: 仅函数内可用,不会被污染,缺: 不可重用

         3). 函数作用域对象原理:

               i. 每个函数定义时都自带好友列表,好友列表里2个格子,一个是空,一个引用window

               ii. 调用函数时临时创建函数作用域对象保存函数局部变量。并将函数作用域对象的地址保存到函数好友列表中离自己近的格子里。

               iii. 函数执行过程中按就近原则先在自己的函数作用域对象中找局部变量使用。如果找不到,才被迫去全局window中找变量使用.

               iv. 函数调用后,好友列表中离自己近的格子清空,导致函数作用域对象以及内部的局部变量被释放!——所以局部变量不可重用!

         b. 作用域链: 保存一个函数所有可用的作用域对象的链式结构(好友列表)学名就叫作用域链。
         1). 作用域链保存着一个函数可用的所有变量

         2). 作用域链控制着变量的使用顺序。先局部后全局。

5. 闭包:  ⏬
         a. 只要希望给一个函数保护一个可反复使用的专属变量,又防止这个变量被外界篡改时,都用闭包。

         b. 闭包三步:

      1). 用外层函数妈妈包裹要保护的变量和内层函数

      2). 外层函数妈妈用return把内层函数孩子返回到外部

      3). 外部想使用内层函数的人,必须调用外层函数,才能获得return出来的内层函数对象。并将内层函数保存在一个变量中反复使用。

         c. 闭包形成的原因: 外层函数调用后,外层函数的作用域对象被内层函数引用着无法释放,形成了闭包对象

         d. 闭包的缺点: 闭包比一般的函数占用多一块内存——外层函数的函数作用域对象。
      所以,用完闭包后,应该尽快释放:
      保存内层函数的变量=null


🆕【后文传送门】👉   js面向对象三大特性之封装—如何创建对象_05


如果这篇【文章】有帮助到你,希望可以给【青春木鱼】点个👍,创作不易,相比官方的陈述,我更喜欢用【通俗易懂】的文笔去讲解每一个知识点,如果有对【前端技术】感兴趣的小可爱,也欢迎关注❤️❤️❤️青春木鱼❤️❤️❤️,我将会给你带来巨大的【收获与惊喜】💕💕! 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

儒雅的烤地瓜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值