函数闭包

闭包(closure)是JavaScript语言的一个难点,也是JavaScript的一个特色,很多高级的应用都要依靠闭包来实现。

    // 1.里面可以访问外面的变量
    var num = 10;
    function outer() {
      console.log(num);
    }
    outer();

    //2. 外面想访问里面的变量怎么办?
    function outer() {
      var num = 10;
      return num; // num + 20;
    }
    var res = outer();
    console.log(res);
    //3. 外面想操作修改这个变量怎么办??
    // 上面的例子只是返回,有多少返回多少,就算你在这里运算也是
    // 返回多少就是多少,也是死的
    function outer() {
      var num = 10;
      function inner(n) {
        num = n;
        console.log(num);
      }
      // 这里依然是内部操作者的
      inner(50)
    }
     outer();

    //4. 终极版 : 外面想操作函数内部的数据
    function outer() {
      var num = 10;
      function inner(n) {
        num = n;
        console.log(num);
      }
      return inner;
    }
   var f =  outer();
   f(60);

闭包的概念

百度百科 : 闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。

维基百科 : 闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数

总结 : 在JavaScript中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。

**闭包中包含了内部函数的代码,以及所需外部函数中的变量的引用 **

产生闭包的条件

// 当内部函数访问了外部函数的变量的时候,就会形成闭包。
// scope 作用域

function outer() {
    var num = 10;
    function inner() {

        console.log(num); // 产生
        console.log(11);  // 不产生
    }
    return  inner;
}

outer()();

闭包的应用

计数器闭包的应用

需求:统计一个函数的调用次数

var count = 0;
function fn(){
  count++;
  console.log("我被调用了,调用次数是"+count);
}
fn();
fn();
fn();

缺点:count是全局变量,不安全。

使用闭包解决这个问题!!!!

function outer(){
  var count = 0;
  function add(){
    count++;
    console.log("当前count"+count);
  }
  return add;
}

var result = outer();
result();
result();
result();

闭包的作用:

  1. 把变量保护起来 ()
  2. 这些变量的值始终保持在内存中 ==> 应用

私有变量

使用闭包实现私有变量的读取和设置

function outer(){

  var num = 10;

  function set_num(n){
    num = n;
  }

  function get_num(){
    return num;
  }

  return {
    set_num:set_num,
    get_num:get_num
  }
}

var obj = outer();
obj.set_num(2000);
console.log(obj.get_num());

【抓娃娃游戏案例.html】

闭包存在的问题

闭包占用的内存是不会被释放的,因此,如果滥用闭包,会造成内存泄漏的问题。闭包很强大,但是只有在必须使用闭包的时候才使用。

js的垃圾回收机制

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Memory_Management

  • 内存:计算机中所有程序的运行都是在内存中进行的,因此内存的性能对计算机的影响非常大,运行程序需要消耗内存,当程序结束时,内存会得到释放。
  • javascript分配内存:当我们定义变量,javascript需要分配内存存储数据。无论是值类型或者是引用类型,都需要存储在内存中。
  • 垃圾回收:当代码执行结束,分配的内存已经不需要了,这时候需要将内存进行回收,在javascript语言中,垃圾回收机器会帮我们回收不再需要使用的内存。

引用记数法清除

引用记数垃圾收集:如果没有引用指向某个对象(或者是函数作用域),那么这个对象或者函数作用域就会被垃圾回收机制回收。

var o = {
  name:"zs"
}
//对象被o变量引用  ,引用记数1
var obj = o;   //变量被o和obj引用,引用记数2

o = 1;  //o不在引用对象了, 引用记数1
obj = null; //obj不在引用对象了,引用记数0,可以被垃圾回收了。

标记清除法清除

使用引用计数法进行垃圾回收的时候,会出现循环引用导致内存泄漏的问题。因此现代的浏览器都采用标记清除法来进行垃圾回收。

这个算法假定设置一个叫做根(root)的对象(在Javascript里,根是全局对象Window)。定期的,垃圾回收器将从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和所有不能获得的对象。

从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。

闭包占用内存释放

function outer(){
  var count = 0;

  function fn(){
    count++;
    console.log("执行次数"+count);
  }
  return fn;
}


var result = outer();
result();
result = null;//当函数fn没有被变量引用了,那么函数fn就会被回收,函数fn一旦被回收,那么outer调用形成的作用域也就得到了释放。

思考: 面试题

  1. 点击不同的按钮,打印对应不同的下标:

          var buttons = document.querySelectorAll('button');
          console.log(buttons);
    

    // 方法1 : buttons[i].index = 1; // 方法2 : 如果面试有需求,只能用闭包 ​
    for ( var i = 0 ; i < buttons.length ; i++) { (function (i) { // var i = i; // 为什么可以了 //1. 这里就相当于 console.log(i) //0---9 //2. 这里是自调用一个外函数,,里面有个i, 然后最里面有个 function // 其实就是一个闭包 //3. 每一次调用否分配一个空间,每个空间里,都有一个变量i 和 一个内函数 buttons[i].onclick = function () { console.log(i); } })(i); }

    
    
    
    
  2. 每隔一秒钟打印一个数字: 1, 2,3,4,5,6,7,8,9,10 ..... setTimeout打印

  for ( var i = 1 ; i <= 10 ; i++) {
    // i 又流失了,,应该保存起来
      // 点击和这个异步都是遍历之后会执行的
    // 闭包

    (function (i) {

        setTimeout(function () {
          
          console.log(i);
          
        },i*500)
      
    })(i); 
  }

正则表达式

正则表达式:用于匹配规律规则的表达式,正则表达式最初是科学家对人类神经系统的工作原理的早期研究,现在在编程语言中有广泛的应用,经常用于表单校验,高级搜索等。

创建正则表达式

构造函数的方式

var regExp = new RegExp(/\d/);

正则字面量

var regExp = /\d/;

正则的使用

/\d/.test("aaa1");

元字符

正则表达式由一些普通字符和元字符组成,普通字符包括大小写字母、数字等,而元字符则具有特殊的含义。

常见元字符

yuan

特殊字符换 http://www.w3school.com.cn/js/js_special_characters.asp

优先级

|`表示或,优先级最低

()优先级最高,表示分组

字符类的元字符

[]在正则表达式中表示一个字符的位置,[]里面写这个位置可以出现的字符。

console.log(/[abc]/);//匹配a,b,c

[^]在中扩号中的^表示非的意思。 是否包括除了XX以外的字符串

//^表示该位置不可以出现的字符
 //[^0]:除了0以外的字符
  console.log(/[^0]/.test("1aaa000"));
  console.log(/[^0]/.test("a"));

[a-z] [1-9]表示范围

console.log(/[a-z]/.test("d"));//小写字母
console.log(/[A-Z]/.test("d"));//大写字母
console.log(/[0-9]/.test("8"));//数字
console.log(/[a-zA-Z0-9]/);//所有的小写字母和大写字母以及数字

边界类元字符

我们前面学习的学习的正则只要有满足的条件的就会返回true,并不能做到精确的匹配。

^表示开头   []里面的^表示取反

$表示结尾

console.log(/^chuan/.test("dachuan"));//必须以chuan开头
console.log(/chuan$/.test("chuang"));//必须以chuan结尾
console.log(/^chuan$/.test("chuan"));//精确匹配chuan

//精确匹配chuan,表示必须是这个
console.log(/^chuan$/.test("chuanchuan"));//fasle

量词类元字符

量词用来控制出现的次数,一般来说量词和边界会一起使用

  //量词用来控制出现次数的

  //console.log(/a/.test("abc"));
  //console.log(/^a/.test("abc"));
  //console.log(/^a$/.test("abc"));
  1. *表示能够出现0次或者更多次,x>=0;
  2. +表示能够出现1次或者多次,x>=1
  3. ?表示能够出现0次或者1次,x=0或者x=1
  4. {n}表示能够出现n次
  5. {n,}表示能够出现n次或者n次以上
  6. {n,m}表示能够出现n-m次

思考:如何使用{}来表示*+?

正则的使用

注意 , 学习目的

正则测试

  1. 验证座机

    • 比如010-12345678 0797-1234567
    • 开头是3-4位,首位必须是0
    • -后面是7-8位
    var phoneReg = /^0\d{2,3}-\d{7,8}$/;
  2. 验证姓名

    • 只能是汉字
    • 长度2-6位之间
    • 汉字范围[\u4e00-\u9fa5]
    var nameReg = /^[\u4e00-\u9fa5]{2,6}$/;
    
  3. 验证QQ

    • 只能是数字
    • 开头不能是0
    • 长度为5-11位
    var qqReg = /^[1-9]\d{4,10}$/;
    
  4. 验证手机

    • 11位数字组成
    • 号段13[0-9] 147 15[0-9] 177[0178] 18[0-9]
    var mobileReg = /^(13[0-9]|147|15[0-9]|17[0178]|18[0-9])\d{8}$/;
    
  5. 验证邮箱

    • 前面是字母或者数字
    • 必须有@
    • @后面是字母或者数字
    • 必须有.
    • .后面是字母或者数字
    var emailReg = /^\w+@\w+(\.\w+)+$/;
    

正则替换 replace

var str = "   123AD  asadf   asadfasf  adf  ";
1  替换掉字符串中的所有空白
2. 将所有的ad替换成xx   i:忽略大小写
  3. 将所有的ad/AD替换成xx

var str = abc,efg,123,abc,123,a
4. 所有的逗号替换成句号

var jsonStr = '[{"name":"张三",score:80},{"name":"张三",score:90},{"name":"张三",score:81}]'; 
5. 把所有成绩都修改成100

正则匹配 match

     //需求:
  var str = "我的手机号是:18511241111, 我的女朋友的手机号是:13211111111,我的前女友的手机号是:18522223333,我的前前女友的手机号是:18511112293";
  //需求:把字符串中所有的手机号找出来。
  console.log(str.match(/1[3-9]\d{9}/g));

正则提取 exec => 数组

    //今天是2018-05-11, 要求;得到年月日;
    var str = "今天是2018-05-11, 要求;得到年月日"

    // 提取
    // var reg = /(\d{4})-(\d{2})-(\d{2})/;
    var reg = /\d{4}-\d{2}-\d{2}/;

字符串总结:

//正则表达式只有:2个: test:测试 exec:提取

reg.test()

reg.exec()

//字符串有两个方法: replace match: 支持参数传正则

str.replace()

str.match();

  //正则的使用:
    //1. 字符串的replace: 正则的替换
    //2. 字符串的匹配:match:  匹配某个字符串中所有符合规律的字符串。
    //3. 正则的测试:test:   表单校验,判断某个字符串是否符合正则的规律
    //4. 正则的提取: 提取匹配的字符串的每一个部分。  ()进行分组

© 2016-2019 kiwi