# javaScript高级

# 1. 核心知识点

  1. 面向对象编程
  2. ES6中的类和对象
  3. 类的继承
  4. 面向对象案例

# 2. 学习目标

  1. 能够说出什么是面向对象
  2. 能够说出出类和对象之间的关系
  3. 能够使用class创建自定义类
  4. 能够说出什么是继承
  5. 能够实现继承的方式

# 3.具体知识点

# 1.编程思想介绍

# 1.1编程思想分类

  • 面向过程(pop)

    • 特点

      面向过程就是分析出解决问题所需要的步骤,然后使用函数一步一步实现,然后在使用的时候在一个一个依次调用
      
    • 举例

      1. webAPI阶段我们写的程序都是面向过程
      2. 实现tab栏导航先分析步骤: 
      	1. 获取元素
          2. 给元素注册事件
          3. 通过函数实现具体的功能
          
      3. 把大象放冰箱分几步:
      	1. 打开门
          2. 装像
          3. 关门
      
  • 面向对象(oop)

    • 特点

      面向对象就是将事物分解为一个一个具体的对象,然后由对象之间分工与合作实现
      
    • 举例

      把大象放冰箱如果用面向对象,使用面向对象实现?
          
      1.先找对象   --- 大象对象  和  冰箱对象
      2.实现对象中的功能
        大象有进去的功能
        冰箱对象有开门和关门的功能
      
    • 总结

      面向对象是以对象划分功能的,而不是步骤
      优点: 代码灵活,易于维护适合多人开发的大型项目
      
    • 特点

      • 封装性

        代码封装成一个函数
        
      • 继承性

        子继承父
        
      • 多态性

        同一个对象,不同时刻具有不同的状态
        
  • 面向过程和面向对象对比

    • 面向对象

      优点: 代码灵活,易于维护,易复用,易扩展
      缺点: 性能较低
      
    • 面向过程

      优点: 性能较高,容易理解
      缺点: 代码灵活性差,复用性低
      

# 2. ES6中的类和对象

# 2.0 什么是es6?

ES6, 全称 ECMAScript 6.0 ,是 JavaScript 的下一个版本标准,2015.06 发版。
ES6 主要是为了解决 ES5 的先天不足,比如 JavaScript 里并没有类的概念,但是目前浏览器的 JavaScript 是 ES5 版本,大多数高版本的浏览器也支持 ES6,不过只实现了 ES6 的部分特性和功能。

# 2.1 ECMAScript发展历史

  • 1997 年 ECMAScript 1.0 诞生。
  • 1998 年 6 月 ECMAScript 2.0 诞生,包含一些小的更改,用于同步独立的 ISO 国际标准。
  • 1999 年 12 月 ECMAScript 3.0诞生,它是一个巨大的成功,在业界得到了广泛的支持,它奠定了 JS 的基本语法,被其后版本完全继承。直到今天,我们一开始学习 JS ,其实就是在学 3.0 版的语法。
  • 2000 年的 ECMAScript 4.0 是当下 ES6 的前身,但由于这个版本太过激烈,对 ES 3 做了彻底升级,所以暂时被"和谐"了。
  • 2009 年 12 月,ECMAScript 5.0 版正式发布。ECMA 专家组预计 ECMAScript 的第五个版本会在 2013 年中期到 2018 年作为主流的开发标准。2011年6月,ES 5.1 版发布,并且成为 ISO 国际标准。
  • 2013 年,ES6 草案冻结,不再添加新的功能,新的功能将被放到 ES7 中;2015年6月, ES6 正式通过,成为国际标准。
  • 从2015年开始,每次es的版本都是以年命名 es2015 es2016...

# 2.1为什么要学类和对象?

  • 原因

    学类和对象本质上就是为了更好的学习面向对象,面向对象更贴近我们的实际生活,我们可以使用对象描述任何一个具体的事物,因为完事万物皆对象
    
  • 举例

    华为P30手机   --- 具体的对象
    小米10手机	  --- 具体的对象
    苹果11手机	  --- 具体的对象
    
    
    手机	---- 抽象   手机是一个泛指,无法使用具体的手机属性描述,但是手机又具有其他每一台具体手机的特性,那么手机就属于一个大的类别, 在程序中就叫类
    
    
    () : 中国人, 外国人
    动物(); 爬行类,水路两栖类,飞行类
    食物(): 能吃的,好吃的,难吃的,有毒的....
    

# 2.2什么是对象?

  • 概念

    万物皆对象,对象是一个具体的事物,看的见摸得着.JS,一个对象是由一组无序的属性和方法组合而成的一个集合
    
  • 属性

    事物的特征,在对象中使用属性表示(名词)
    
  • 方法

    事物的行为或者功能,在对象中使用方法表示(动词)
    

# 2.3什么是类?

  • 通俗理解概念

    一类人,一类事物. 类就是将很多具体的对象中公共的部分抽象成一个整体(模板).
    本质: 将对象中公共的方法或者属性封装成一个模板
    作用: 通过类可以实例化一个对象.  其实我们基础阶段所学的构造函数就可以称为是一个类(构造函数中就是一些公共的信息)
    
  • 类的作用

    类的作用和构造函数的作用一样,也是用来创建对象
    

# 2.4总结类和对象

  • 类是一个泛指的,是一个抽象的具有公共属性的集合.  人类(包含了所有人的基本特征,有五官,会吃饭,会搞对象)
    
  • 对象

    对象是一个具体的事物,之前说对象是通过构造函数创建的,从今以后大家要注意了,构造函数就是一个特殊的类,对象都是通过类创建的, (类是爹,对象是儿子)
    

# 3.创建类

# 3.1 语法

class 自定义类名 { }

//创建对象(实例化一个对象)
var 自定义对象 = new 类名()

# 3.2注意事项

  • 通过类创建对象必须使用new关键字
  • 类的命名规范和构造函数的命名规范一样(帕斯卡命名)
  • 创建对象的时候,类名后必须加小括号

# 3.3课堂练习

  • 创建一个明星类 和 对象

    class Start {
    }
    
    var ldh = new Start();
    

# 3.4 如何类中设置属性

  • 通过constructor构造函数设置属性

    语法:
    class  自定义类名 {
        constructor(形参, 形参) {
        }
    }
    
  • constructor详解

    1. constructor 叫构造函数,是类中默认的.
    2. constructor 构造函数用来接收创建对象时候传递的参数,创建对象并返回
    3. 通过 new + 类名创建对象的时候,程序就会自动调用类中 constructor
    4. 如果我们在类中没有定义 constructor 函数,程序在执行的时候也会自动创建的
    
  • 注意事项

    class 类中所有的函数都不需要写 function 关键字
    
  • 课堂练习演示

    创建一个明星类
    class Start {
    	//constructor 函数就是用来给对象设置属性的
    	constructor(uName) {
    		this.uName = uName;
    	}
    }
    //实例化一个对象
    var ldh = new Start('刘德华');
    console.log(ldh);
    
  • 分析代码过程

    1593005140167

  • 总结

    1. 创建类使用 class 关键字,类的自定义名称复合帕斯卡命名法
    2. 类中的构造函数 constructior 用来创建对象并给对象设置属性
    3. 通过 new 加类名创建对象时候,程序会自动调用 constructior构造函数
    4. 创建对象使用类名,类中的所有函数都不加 function 关键字

# 3.5如何在类中设置方法

  • 语法

    class 类名 {
        
        //构造函数,设置属性
        constructor() {
            
        }
        方法名() {
            
        }
    }
    
  • 语法详解注意事项

    1. 在类中直接设置方法,方法名前不需要添加function关键字
    2. 在类中函数之间不需要有任何的分割符
  • 课堂案例练习

    使用类的方式创建几个对象 (父母,男女朋友,喜欢的英雄人物...)
    

# 3.类继承

# 3.1 什么是继承?

  • 通俗理解

    子承父业,孩子继承父辈的资源(,,房子,颜值...), 历史世袭制  
    
  • 程序角度理解

    子类继承父类中的属性和方法
    特别注意:
    继承指的是类与类之间的关系,不是对象和类之间的关系,也不是对象和对象之间的关系
    

# 3.2 为什么学继承?

  • 举例

    //中国学生类别
    class C_Student {
    	constructor(lg) {
    		this.language = lg;
    	}
    	ks() {
    		alert('考外语');
    	}
    }
    
    //外国学生类别
    class A_Student {
    	constructor(lg) {
    		this.language = lg;
    	}
    	ks() {
    		alert('考汉语');
    	}
    }
    
    var zs = new C_Student('张三');
    var ls = new A_Student('李四');
    
  • 作用

    子类继承父类中的方法和属性,减少重复代码
    

# 3.3如何实现继承?

  • 语法

    class 子类名  extends 父类 {
        
    }
    
  • 语法详解

    1. 在es6中类与类之间的继承使用 extends关键字
    2. 子类继承父类后,子类就可以使用父类中的属性和方法
  • 课堂演示类继承

    //父类
    class Student {
    	ks(ksName) {
    		alert(ksName);
    	}
    }
    //子类继承父类
    class C_student extends Student {}
    class A_student extends Student {}
    var zs = new C_student();
    var ls = new A_student();
    zs.ks('汉语');
    ls.ks('英语');
    

# 3.4super关键字介绍

  • 代码举例演示

    class Father {
    	constructor(uName) {
    		this.uName = uName;
    	}
    	money() {
    		console.log(this.uName + '有100万');
    	}
    }
    class Son extends Father {
    	constructor(uName) {
    		this.uName = uName;  //指向的是 子类创建的对象
    	}
    }
    var lz = new Father('我是爹');
    lz.money(); // money 中的this 指向的是 父类的对象
    
    var zs = new Son('我是儿子');
    zs.money();   //会报错   
    
  • 如何解决?

    在es6中规定,如果子类继承父类,则需要在子类的构造函数 constructor 中通过 super 关键字来调用父类中的构造函数
    
    class Father {
    	constructor(uName) {
    		this.uName = uName;
    	}
        money() {
    		console.log(this.uName + '有100万');
    	}
    }
    class Son extends Father {
    	constructor(uName) {
    		super(uName);
    	}
    }
    
    var lz = new Father('我是爹');
    lz.money(); // money 中的this 指向的是 父类的对象
    
    var zs = new Son('我是儿子');
    zs.money();   //不会报错    super() 会自动调用父类中的构造函数并将对应的值传递给父类
    
  • super关键字另外一种用法

    1. 子类除了在构造函数中通过 super 调用父类的构造函数外
    2. 子类还可以通过 super 关键字调用父类中的普通函数
    例如:
    
    class Father {
    			constructor(uName) {
    				this.uName = uName;
    			}
    
    			money() {
    				console.log('老子能赚大钱');
    			}
    }
    
    class Son extends Father{
    	constructor(uName) {
    		super(uName);
    	}
    	money() {
    		console.log('儿子我也能赚钱');
    				//通过super关键字调用父类中的普通函数
    		super.money();
    	}
    }
    
    var zs = new Son('张三');
    zs.money();
    
  • super关键字总结

    1. 子类继承父类时候,子类构造函数中必须设置 super关键字(如果子类中没有构造函数则不需要)
    2. 子类中如果需要调用父类中的普通函数,可以通过 super.函数名()实现调用
    3. 在继承中方法的执行是按照就近原则执行的,如果子类中有方法就先执行子类中的如果没有就执行父类中的方法

# 3.5 课堂案例

1. 父类中实现一个加法计算,子类继承父类该方法
2. 父类中实现一个加法计算,子类继续扩展一个减法计算

# 3.6super关键字和 this 关键字

  • 代码演示如下:

    class Fater {
    	constructor(uName) {
    		this.uName = uName;
    	}
    	money() {
    		alert('老子很有钱');
    	}
    }
    class Son extends Fater{
    	constructor(uName) {
            //必须放到this关键字前
            super(uName);
    		this.uName = uName;
    	}
    	subMoney() {
    	  alert('儿子我能能花钱');
    	}
    }
    var zs = new Son('张三');
    	zs.subMoney();
    
    //注意: 
    // 1. zs 这个对象要想使用父类中的money方法,就必须使用 extends 关键字继承父类
    // 2. 当继承父类的时候,必须在子类的构造函数中使用super调用父类的构造函数
    // 3. 使用super调用父类构造函数的的时候必须放到this关键字之前,(老子优先,要尊老爱幼)
    // 4. 继承程序就是要先执行父类中的构造函数,然后在执行子类中的构造函数,所以必须写到开始位置
    zs.money();
    

# 3.7类使用注意属性总结

  • 在ES6中没有变量提升,所以必须先定义类,然后才能通过类创建对象

  • 类中共有属性或者方法必须加this 调用

    class Father {
    	constructor(uName) {
    		this.uName = uName;
    	}
    	eat() {
    		alert('正在吃饭');
            
            //报错
            //1. uName 是constructor函数中的一个形参,在eat另外一个函数中是无法访问其他函数中的参数的
            //2. 修改方式通过 this.uName
            //3. 访问当前对象身上的属性,要加this
    		console.log(uName);
            
            
            //报错
            //1. 要访问当前对象中的方法,也需要加this关键字,dance是属于对象的,所以必须加this
            //2. 修改方式方法前 设置this关键字  this.dance()
    		dance();
    	}
    	dance() {
    		alert('吃完饭去跳舞');
    	}
    }
    var zs = new Father('张三');
    zs.eat();
    
  • 构造函数中的this 和 方法中的this指向问题

    1. 构造函数中的this指向的就是当前类创建的对象
    2. 方法中的this指向当前方法的调用者

# 4. 面向对象版tab栏切换

  • 功能

    1. 切换功能
    2. 添加功能
    3. 关闭功能
    4. 修改功能
  • 搭建HTML结构设置样式

  • 实现功能思路

    1. 将整个tab栏抽象成一个对象

      //将tab栏抽象成一个类
      class Tab {
          constructor(id) {
              //获取当前tab对象
               this.tab = document.querySelector(id);
               //获取所有的li
               this.lis = document.querySelectorAll('li');
                //获取所有的内容盒子
               this.content = document.querySelectorAll('.content');
          }
      }
      //通过类创建tab栏对象
      new Tab("#tab");
      
    2. 设置一个init方法,初始化给每一个li注册点击事件

      constructor(id) { 
      	...
          //创建对象的时候立即调用
      	this.init();
      }
      init() {
        for(var i = 0; i < this.lis.length; i++) {
      		//给每一个li绑定一个索引号
      		this.lis[i].index = i;
      		//演示给每一个li注册一个事件
      		this.lis[i].onclick = function() {
      			console.log(this.index);
      		}
            
            	//当要实现真正的切换功能的时候,要调用切换功能的方法实现
              //如下:
              this.lis[i].onclick = this.toggleTab;
      	}
      }
      
    3. tab栏对象有切换功能

      //that 用来保存构造函数中的this
      var that;
      class Tab {
          constructor(id) { 
          	that = this;
          }
          init() {
              this.lis[i].onclick = this.toggleTab; 
          }
          
          //切换方法
          toggleTab() {
              
              //这样写会报错:
              //原因: this.lis.length 中的this 指向的是当前方法的调用者 li
              //而当前li是一个具体的元素对象,不是一个伪数组,所以不支持length属性
              //这里我们是希望获取构造函数中的this, 所以在类的外部定义一个全局变量保存一下构造函数中的		//this就可以了
              for(var i = 0; i < this.lis.length; i++) {
                  this.lis[i].className = '';
              }
              
              //正确写法:
              for(var i = 0; i < that.lis.length; i++) {
                  that.lis[i].className = '';
              }
              this.className = 'liactive';
              
              //显示对应的内容
         		for(var i = 0; i < that.content.length; i++) {
      			that.content[i].style.display = 'none';
      		}
      		that.content[this.index].style.display = 'block';
          }
      }
      
    4. tab栏对象有添加功能

      思路:
      1. 点击添加按钮
      2. 创建一个 li 和 一个新的内容
      2. 将创建好的元素添加到对应的父元素中
      
      具体实现:
      1.在构造函数中先获取 添加按钮
      constructor(id) { 
          ...
      	//获取添加按钮
      	this.addBtn = document.querySelector('.tabadd span');
      }
      
      2.在init函数中给添加按钮绑定点击事件
      init() { 
      	// 点击添加
      	this.addBtn.onclick = function() {  }
          //注意:
          //init这个函数是在构造函数中调用的,所以这个this 就是构造函数中的this
          //演示的时候在点击事件后面添加一个匿名函数,
          
          //实际操作的时候,要先添加一个 addTab函数,然后调用
          //如下:
          this.addBtn.onclick = this.addTab;
      }
      
      
      3. 添加一个 addTab 函数  
      //创建元素,追加元素,先代码演示 如何通过 insertAdjacentHTML 追加元素
      addTab() {
       
       //调用清除样式的公共方法 (先自己写,然后抽象成一个这样的方法)
       that.clearClass();  
          
       //1.创建li标签
       var li = '<li><span>测试3</span><span class="iconfont icon-guanbi"></span></li>';
      
       //2.将创建好的li添加到ul中(备注: 一定要在构造函数中先获取ul)
       that.ul.insertAdjacentHTML('beforeend', li);    
       
       //3.创建内容区域
       var div = '<div class="content">新内容</div>';
          
        //4. 将内容区域添加到内容区域中(备注: 一定要在构造函数中获取内容区域)
        that.tabscon.insertAdjacentHTML('beforeend', div);
          
        //5. 添加元素后,后天添加的元素并没有对应的事件,是因为给元素绑定事件的时候,在一开始就做了,当前我们动画创建元素后,元素并没有注册事件,所以需要在点击按钮的时候,需要从新获取一下页面的元素,并重新注册实现就可以解决
          
      	//5.1 首先要将构造函数中获取li 和 内容的元素单独剪切到一个函数中 getNode
          //5.2 在 init函数中调用 getNode这个函数
          //5.3 在当前函数中再调用 init函数就可以
          that.init();
      }
      
      4. 添加一个 clearClass 函数,清除公共样式
      clearClass() {
          for(var i = 0; i < this.lis.length; i++) {
      		this.lis[i].className = '';
      		this.content[i].style.display = 'none';
      	}
      }
      
      5.1 添加一个单独用来获取 li 和 内容的函数
      getNode() {
          //获取所有的li
      	this.lis = document.querySelectorAll('li');
      	//获取所有的内容盒子
      	this.content = document.querySelectorAll('.content');
      }
      
    5. tab栏对象有删除功能

      思路:
      1. 点击 删除 按钮,删除对应的li 及内容区域
      2. 点击 删除 按钮, 删除按钮的索引号就是其父元素对应的索引号
      
      
      实现:
      1.getNode() 函数中先获取所有的删除按钮 (如果在构造函数中获取,会报错,因为要实时获取页面中的元素)
      
      getNode() {
          //获取所有的li
      	this.lis = document.querySelectorAll('li');
      	//获取所有的内容盒子
      	this.content = document.querySelectorAll('.content');
          //获取所有的删除按钮
      	this.delBtns = document.querySelectorAll('.icon-guanbi');
      }
      
      
      2. 在init函数中记得要给每一个删除按钮注册一个点击事件
      init() {
      	this.getNode();
      	// 点击添加
      	for(var i = 0; i < this.lis.length; i++) {
      		//给每一个li绑定一个索引号
      		 this.lis[i].index = i;
      		 //给每一个li注册一个事件, 调用切换功能
      		 this.lis[i].onclick = this.toggleTab;
      		  
              //给每一个删除按钮注册一个点击事件
      		this.delBtns[i].onclick = this.removeTab;
      	}
      	this.addBtn.onclick = this.addTab;
      }
      
      3. 定义一个 removeTab 删除函数
      
      removeTab(e) {
          //首先要阻止事件冒泡
          e.stopPropagation();
          
          //获取当前删除按钮的索引 = 父元素的索引
          var index = this.parentNode.index;
          
          //删除当前li
          that.lis[index].remove();
          
          //删除当前对应的内容
          that.content[index].remove();
          
          //删除后从新给页面中的元素绑定一次事件
          that.init();
          
          //问题1: 删除元素后,应该让前一个元素是选中的状态(前一个元素被点击过)
          //解决如下:
          index--;
          that.lis[index].click();
          
          //问题2: 如果用户直接点击的就是第一个,那么会报错,因为第一个索引是0, index-- 就变成-1
          //解决如下
          index < 0 ?  null : that.lis[index].click();
          
          //问题3: 如果用户点击的不是当前选中的,那么就不需要修改样式了,直接将当前元素移除
          //解决如下:
          if(document.querySelector('.liactive')) {
      		return;
      	}else {
      		//删除当前,前一个元素设置选中状态
      		index--;
      		index < 0 ?  null : that.lis[index].click();
      	}
      }
      
      
    6. tab栏对象有编辑功能

      思路:
      1. 双击实现编辑, 双击事件 ondblclick
      2. 双击禁止选中文字  
      window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
      
      css实现禁止文字选中:
       -webkit-touch-callout: none;
       /* iOS Safari */
      -webkit-user-select: none;
       /* Chrome/Safari/Opera */
      -khtml-user-select: none;
      /* Konqueror */
      -moz-user-select: none;
       /* Firefox */
      -ms-user-select: none;
      /* Internet Explorer/Edge */
      user-select: none;
      
      
      
      3. 双击时候,动态创建一个输入框, 将当前标签中的文字赋值给输入框
      4. 当鼠标离开时候,将输入框中的值赋值给标签
      
      
      实现:
      
      1.getNode() 中 获取所有的 span (不能写到构造函数中)
      getNode() {
          ...
      	//获取li 中所有第一个span
      	this.spans = document.querySelectorAll('.firstnav li span:nth-child(1)');
      }
      
      2.init() 函数中给每一个span 标签注册双击事件
      init() {
          for(var i = 0; i < this.lis.length; i++) {
              ...
      		//每一个span标签注册双击事件
      		this.spans[i].ondblclick = that.editTab;
      	}
      }
      
      3.新建一个 editTab函数 
      editTab() {
          //1.阻止双击选中文字
          window.getSelection ? window.getSelection().removeAllRanges() : 			document.selection.empty();
          
          //2. 获取span标签中的文字
          var value = this.innerHTML;
          
          //3. 创建一个input 标签,并赋值
          var input = '<input type="text" value='+value+'>';
          
          //4. 将input 标签添加到span标签中
          this.innerHTML = input;
          
          //5.双击标签时候,默认选中输入框的文字
          var input1 = this.children[0];
          input1.select();
          
          //6. 当鼠标离开时候,将输入框中的值赋值给span标签
          input1.onblur = function() {
      		this.parentNode.innerHTML = this.value;
      	}
          
          //7. 当按下回车时候,当输入框中的值赋值给span标签
          input1.onkeyup = function(e) {
      		if(e.keyCode == 13) {
      			this.blur();
      		}
      	} 
      }
      

# javaScript高级

# 1. 核心知识点

  1. 构造函数原理
  2. 原型
  3. 构造函数+原型实现继承

# 2. 学习目标

  1. 能够使用构造函数创建对象
  2. 能够说出原型的作用
  3. 能够说出访问成员的规则
  4. 能够使用ES5新增的方法

# 3. 内容介绍

# 1. 构造函数和原型

# 1.1概述

  • 昨天学习了面向对象,在js中通过类创建对象,通过 extends 实现类的继承
  • 在es5之前 js 并没有类的概念,接下来我们学习一下 构造函数 和 原型实现面向对象 + 继承

# 1.2回顾创建对象方式

  • 通过字面量方式创建对象

    var obj =  {}
    
  • 通过内置构造函数创建对象

    var obj = new  Object();
    
  • 通过自定义构造函数创建对象

    function Start(uName) {
        this.uName = uName;
        this.sing = function() {
            console.log('正在唱歌...');
        }
    }
    var ldh = new Start('刘德华');
    var zxy = new Start('张学友');
    
  • 构造函数

    1. 构造函数是一个特殊的函数,用来创建对象
    2. 构造函数使用 new 关键字创建对象
    3. 构造函数创建对象的4件事情
    

# 1.3 静态成员和实例成员

  • 成员

    构造函数中的方法和属性都叫成员
    例如:
    function People(uName) {
        this.uName = uName;
        this.sing = function() {
            
        }
    }
    
    uName属性就是一个成员
    sing 方法就是一个成员
    
    构造函数中的成员分为: 实例成员 和 静态成员
    
  • 实例成员

    实例成员就是构造函数内部,通过this关键字添加(绑定的)成员, 比如 uName, sing
    实例成员的特点是: 实例成员只能通过实例化的对象才能访问
    
    例如:
    function People(uName) {
        this.uName = uName;
        this.sing = function() {
            
        }
    }
    
    //要访问 uName实例成员,必须先实例化对象,如下所示:
    var ldh = new People('刘德华');
    ldh.uName
    
  • 静态成员

    静态成员,在构造函数身上直接添加的成员
    
    例如:
    function People(uName) {
        this.uName = uName;
        this.sing = function() {    
        }
    }
    // gender 就是静态成员
    People.gender = '男'; 
    
    静态成员特点: 静态成员只能通过构造函数访问,不能通过实例成员访问
    

# 2.构造函数的问题

# 2.1 构造函数存在浪费内存的问题

  • 通过构造函数可以很方便的创建出一个或者多个对象

  • 但是在创建对象的过程中存在严重的内存浪费

    代码举例:
    function People(uName) {
        this.uName = uName;
        this.sing = function() {    
        }
    }
    
    var ldh = new People('刘德华');
    var zxy = new People('张学友');
    
    1. ldh 和 zxy 这两个对象都有一个公共的方法 sing
    2. 通过打印对边这两个 sing的结果不相等
    console.log(ldh.sing === zxy.sing)
    3. 原因就是 sing 是一个方法,也是一个对象,那么会在内存中重新开辟新的空间位置
    

    1593076776219

# 2.2 如何解决对象使用同一个函数,不开辟新的内存空间?

通过原型对象 prototype 来解决

# 3. prototype介绍

# 3.1 概念

js中规定,每一个构造函数都有一个 prototype属性[该属性本质上就是一个对象]

# 3.2 作用

通过原型对象 prototype 可以用来设置共享的方法,对象的实例就可以使用这些共享的方法

# 3.3 代码演示

function People(uname) {
	this.uname = uname;
	this.sing = function() {
		alert('正在唱歌');
	}
}
console.dir(People);

1593077756887

# 3.4 通过原型设置共享方法

function People(uName) {
	this.uName = uName;
}
//给原型对象添加公共的方法
//因为prototype 就是一个对象,所以给对象添加属性和方法直接通过点的方式
People.prototype.sing = function() {
	 		alert('我会唱歌');
}

var ldh = new People('刘德华');
var zxy = new People('张学有');

ldh.sing();
zxy.sing();

console.log(ldh.sing === zxy.sing);

# 3.5 总结

  • 原型是什么? 原型是一个对象 prototype
  • 原型的作用是什么? 设置公共方法的
  • 一般情况下公共属性定义到构造函数中,公共的方法定义到原型对象中
  • 我们es6中,构造函数中就是在初始化公共的属性,而方法都在构造函数外部

# 4.对象原型__proto__

# 4.1 为什么对象的实例可以访问构造函数原型对象上的方法?

  • 每一个对象都会有一个 __proto__对象,该对象指向的就是构造函数中的 prototype

  • 代码演示

    function People(uname) {
    			this.uname = uname;
    }
    People.prototype.sing = function() {
    			alert('我会唱歌');
    }
    var ldh = new People('刘德华');
    console.dir(ldh);
    
    console.log(ldh.__proto__ === People.prototype);  // true
    

    1593079247643

  • 方法查找规则

    1. 首先先看当前实例对象身上是否有该方法,如果有就执行对象身上的方法
    2. 如果没有方法,那么对象会通过 __proto__ 去构造函数原型对象 prototype 查找
  • 构造函数 原型对象prototype 对象原型__proto__ 之间的关系

    1593079803074

  • 注意事项

    prototype  称为原型对象
    
    __proto__  称为对象原型       (对象原型指向原型对象)
    

# 5. consturctor构造函数

# 5.1 什么是constructor?

对象原型(__proto__)原型对象(prototype) 中都有一个 constructor属性, constructor称为构造函数

# 5.2 代码演示

function People(uname) {
	this.uname = uname;
}
console.dir(People);

var ldh = new People('刘德华');
console.dir(ldh);


//打印
console.log(People.prototype.constructor);
console.log(ldh.__proto__.constructor);
//总结:
constructor 就是用来记录对象通过哪个构造函数创建的,指向原来的构造函数

1593080456859

# 5.3 课堂案例分析

1. 
function People(uname) {
	this.uname = uname;
}

People.prototype.sing = function() {
	alert('正在唱歌');
}

People.prototype.dance = function() {
    alert('正在跳舞');
}
var ldh = new People('刘德华');
//1. 如上写法, People.prototype.constructor 指向谁?
console.log(People.prototype.constructor);  // 构造函数 People



2.
function People(uname) {
	this.uname = uname;
}
People.prototype = {
	sing: function() {
		alert('正在唱歌..');
	},
	dance: function() {
		alert('正在跳舞..');
	}
}
var ldh = new People('刘德华');
//1. 如上写法, People 原型对象构造函数指向谁?   // Object()
console.log(People.prototype.constructor);
//原因: 是将一个对象赋值给 People.prototype. 而我们之前知道字面量创建对象的本质就是内置构造函数创建对象 


//2. 如何解决让构造函数指向当前构造函数 People?
People.prototype = {
    //利用constructor 指回原来的构造函数
	constructor: People,
	sing: function() {
		alert('正在唱歌..');
	},
	dance: function() {
		alert('正在跳舞..');
	}
}

# 6. 构造函数,实例,原型对象之间的关系

1593088161117

  • 总结
    1. 构造函数中原型对象 prototype 用来设置公共的方法
    2. 原型对象中的构造函数 constructor 指向当前构造函数
    3. 构造函数创建的实例,实例对象中对象原型 __proto__ 指向 原型对象 prototype, 所以实例对象可以访问构造函数中设置的公共方法
    4. 实例对象中的 __proto__中的构造函数 constructor 又指向了构造函数

# 7.原型链

# 7.1. 为什么要学原型链?

分析如下代码:

function People(uname, age) {
	this.uname = uname;
	this.age = age;
}
var ldh = new People('刘德华', 35);

console.log(ldh.age);
console.log(ldh.age.toString());

//问题: 当前对象 ldh 为什么可以调用toString() 这个方法? 当前对象中也没有toString方法,那是如何做到的?

# 7.2 什么是原型链?

原型链: 多个对象原型 `__proto__` 之间形成的一个链装结构

function People(uname, age) {
	this.uname = uname;
	this.age = age;
}
var ldh = new People('刘德华', 35);

console.dir(ldh);

1593089546574

1593090165093

# 7.3 回答问题

1. 方法的查找规则,如果当前对象中有方法,则执行该对象的方法,如果没有那么会 沿着 prototype 继续查找
2. 当前对象 ldh没有 toString方法, 那么就 通过 __proto__ 到 原型对象上查找,如果没有
3. 继续沿着 原型对象.__proto__ 向上查找,发现 Object原型对象上有 toString 方法
4. 所以当前对象可以执行toString 方法,这就是原型链带来的好处

# 7.4对象成员查找规则

  • 当访问一个对象的属性或者方法的时候,先查找当前这个对象自身有没有该属性或者方法
  • 如果没有,就找他的原型 __proto__ 指向的 prototype
  • 如果还没有就继续找原型对象的原型 __proto__ 指向的 prototype
  • 依次类推,如果找到Object还没有,那么就返回 null

# 8.原型对象中的this指向

# 8.1 this指向总结

  • 普通函数中的this

    function fn() {
        console.log(this);  // this 指向的是window ,  调用当前函数是通过 window.fn()
    }
    
  • 构造函数中的this

    function People() {
        this.name = '张三';   // this 指向的是构造函数最后创建的实例对象
    }
    
  • 方法中的this

    function People() {
        this.eat = function() {
             this;       // this 指向当前方法的调用者 -> 构造函数实例对象
        }
    }
    
  • 事件中的this

    div.onclick = function() {
        this;     // this 指向当前事件源对象
    }
    
  • 原型对象中的this

    function People() { }
    
    People.prototype.sing = function() {
         this;    // this指向当前方法调用者, 构造函数实例对象
    }
    var ldh = new People();
    ldh.sing();
    

# 9. 通过原型对象应用

# 9.1 通过原型对象扩展方法

//内置对象 数组中有很多方法,但是就是没有求和的方法,通过原型对象学习,我们可以给任意的一个内置对象拓展方法
1. 先打印数组构造函数原型对象
console.log(Array.prototype);

2. 通过原型对象给数组扩展一个求和方法,如下:
Array.prototype.sum = function() {
    var sum = 0;
    for(var i = 0; i < this.length; i++) {
        sum += this[i];
    }
    return sum;
}
var ary = [1, 2, 3];
console.log(ary.sum());

# 10.ES5原生继承原理

# 10.1 es5继承

es5中没有类的概念,不支持 extends 关键字实现继承,但是继承是面向对象的最主要特征,es5中实现继承通过构造函数+原型对象 来模拟实现继承 称为组合继承

# 10.2 学es5原生继承,先学call()

  • call() 使用一: 可以调用函数

    function fn() {
        alert(24);
    }
    
    fn.call();  //调用函数
    
  • call() 使用二: 可以修改this的指向

    function fn() {
        console.log(this);   // this 默认指向window对象
    }
    
    //如果要将 fn 中的this指向其他对象, 例如指向 ldh 这个对象,实现如下:
    
    var ldh = {
        name: '刘德华'
    }
    
    fn.call(ldh);   // 此时就将fn函数中的this 指向了 ldh 这个对象
    

# 10.3 利用构造函数继承父类型属性

function  Father(uname) {
     //this 指向的是Father 创建的对象
	 this.uname  = uname;
}
function Son(uname) {
    // this 指向当前Son创建的对象
	Father.call(this, '张三');
}
var s1 = new Son('张三');
console.log(s1);
  • 分析:
    1. Father 构造函数中的this 指向的是当前 构造函数创建的实例对象
    2. Son 构造函数中的this 指向的是当前Son 构造函数创建的实例对象
    3. 由于Son中没有通过this关键字绑定属性,那么就要借用Father构造函数中的this,但是Father中的this指向的是Father创建的实例
    4. 通过 call()在调用Father构造函数时候,将Father中的this 指向当前 Son中的this,实现继承
  • 通过断点调试演示过程

# 10.4 利用原型对象继承父类型方法

  • 不严谨的继承代码分析演示

    function Father(uname) {
    	this.uname = uname;
    }
    Father.prototype.money = function() {
    	alert(1230000);
    }
    function Son(uname) {
    	//继承父类中的属性
    	Father.call(this, uname);
    }
    var zs = new Son('小张');
    console.log(zs);
    
    //按照原型链的逻辑,查找方法的规则,当前对象如果没有方法,那么就要通过 __proto__ 去原型对象上找,对应的图形
    

    1593137571399

    function Father(uname) {
    	this.uname = uname;
    }
    
    Father.prototype.money = function() {
    	alert(1230000);
    }
    
    function Son(uname) {
    	//继承父类中的属性
    	Father.call(this, uname);
    }
    
    // 将 Father的原型对象赋值给当前 Son 原型对象
    Son.prototype = Father.prototype;
    
    //分析:
    //1. 这样Son 实例对象是可以继承父类中的方法
    //2. 当给Son 原型对象设置方法的时候,Father 中也有了, 因为 是两个对象的赋值,地址是相同的
    Son.prototype.kaos = function() {
    	alert('儿子要参加考试');
    }
    // 3. 发现父类中也有考试这个方法,就是因为 Father原型对象现在和 Son原型对象使用了同一个内存地址导致的
    console.log(Father.prototype);
    
    
    //结论: 这样的继承是不规范的,要避免!!!!
    
  • 合理的继承代码演示

    function Father(uname) {
    	this.uname = uname;
    }
    Father.prototype.money = function() {
    	alert(1230000);
    }
    function Son(uname) {
    	//继承父类中的属性
    	Father.call(this, uname);
    }
    
    Son.prototype = new Father();
    
    var zs = new Son('小张');
    
    //配图分析:
    //1. Father 实例对象 通过__proto__ 得到 原型对象的方法
    //2. 将Father 实例对象赋值给 Son原型对象,那么Son原型对象就可以借用Father 实例对象的 __proto__访问 money 方法
    //3.此是单独给 Son原型对象添加方法,也不会影响 Father原型对象了,因为现在已经是两个不同单独的对象了
    Son.prototype = new Father();
    Son.prototype.kaos = function() {
    	alert('儿子要参加考试');
    }
    
    //有新的问题出现, 当前 zs 的构造函数指向了Father  zs.__proto__.constructor
    //zs 这个对象是通过 Son 构造函数创建的, zs.__proto__.constructor 应该指向的是 Son构造函数
    
    //思考为什么显示却是 Father 构造函数?
    
    //分析:
    //1.  Son.prototype = new Father();
    //2.  Son.prototype.constructor 就指向了 Father 这个构造函数
    
    //解决:
    Son.prototype = new Father();
    
    //通过constructor 将构造函数执向当前Son构造函数即可
    Son.prototype.constructor = Son;
    
    Son.prototype.kaos = function() {
        alert('儿子要参加考试');
    }
    

    1593138505453

# 11.Es6类实现继承,类的本质

类的本质: 就是一个函数 , es6中的类就是构造函数的另外一种写
class People { }
console.log(typeof People);

1. es5 构造函数都有原型对象  ----> es6中类 也有原型对象
//console.log(People.prototype);

2. es5 构造函数原型对象都有 constructor, constructor指向当前构造函数  ----> es6中类也有
//console.log(People.prototype.constructor);

3. es5 构造函数可以通过原型对象添加方法  --- es6 也可以
// People.prototype.sing = function() {alert(123);}
// var zs = new People();
// zs.sing();

4. es5 对象原型__proto__ 指向原型对象  --- es6 也可以

// var zs = new People();
// console.log(zs.__proto__ === People.prototype);

# 12. 新增的方法

  • 数组方法

    1. forEach 遍历数组
    数组名.forEach(function(value, index, arry){ })
    // 1. value  代表数组中的值
    // 2. index  代表数组中的索引值
    // 3. arry   代表当前数组
    
    //案例演示: 通过forEach遍历数组求和
    var ary = [1, 2, 3, 4];
    var sum = 0;
    ary.forEach(function(value){
    	sum += value;
    })
    console.log(sum);
    
    1. filter 筛选数组,返回数组

      数组名.filter(function(vale, index, arry){ })
      
      1. value 代表数组中的值
      2. index 代表数组中值的索引
      3. 代表当前数组
      
      注意: filter 筛选数组,内部还是要遍历数组的,但是最主要的是用来筛选条件的,将满足条件的返回一个数组
      
      // 代码演示: 将数组中的偶数返回.
      	  var ary = [1, 2, 3, 4, 0, 5, 6, 7, 8, 9, 0,];
      		var newAry = ary.filter(function(value){
      			return  value % 2 == 0;
      		});
      		console.log(newAry);
      // 将数组中的非0数字返回  
      		var ary = [1, 2, 3, 4, 0, 5, 6, 7, 8, 9, 0,];
      		var newAry = ary.filter(function(value){
      			return  value != 0;
      		});
      		console.log(newAry);
      //将数组中能够被3整除的数返回
      	
      		var ary = [1, 2, 3, 4, 0, 5, 6, 7, 8, 9, 0,];
      		var newAry = ary.filter(function(value){
      			return  value % 3 == 0;
      		});
      		console.log(newAry);
      
    2. some() 查找数组中是否有满足条件的元素,返回布尔类型

      数组名.some(function(value, index, arry){});
      
      1. value 代表当前数组中的值
      2. index 代表当前数组中的索引
      3. arry 代表当前数组
      
      //代码演示:  数组中是否有 大于等于20的值
      var ary = [1, 2, 3, 4, 0, 5, 6, 7, 8, 9, 0,];
      var res = ary.some(function(value){
      	 return value >= 5;
      })
      console.log(res);
      

# 13. 查询商品案例

  • 根据数据动态显示

    1. 准备HTML结构,自己写

    2. 在js中先定义数据格式

      var  data = [
      			{
      				id: 1,
      				pname: '小米手机',
      				price: 1299
      			},{
      				id: 2,
      				pname: '华为手机',
      				price: 4299
      			},{
      				id: 3,
      				pname: '华为平板',
      				price: 12999
      			},{
      				id: 4,
      				pname: '苹果手机',
      				price: 5299
      			},{
      				id: 6,
      				pname: '锤子手机',
      				price: 998
      			},{
      				id: 7,
      				pname: '一加手机',
      				price: 4399
      			},{
      				id: 8,
      				pname: '诺基亚手机',
      				price: 456
      			}
      		]
      

# javaScript高级

# 1. 核心知识点

  1. 函数的定义和调用
  2. this指向
  3. 严格模式
  4. 高阶函数
  5. 闭包
  6. 递归

# 2. 学习目标

  1. 能够说出函数的多种定义和调用方式
  2. 能够说出和改变函数内部this的指向
  3. 能够说出严格模式的特点
  4. 能够把函数作为参数和返回值传递
  5. 能够说出闭包的作用
  6. 能够说出递归的条件
  7. 能够说出深拷贝和浅拷贝

# 3. 开始学习

# 1. 函数定义

# 1.1通过function

function fn() {}  命名函数

# 1.2通过函数表达式

var fn = function() {}  匿名函数

# 1.3 通过构造函数定义

var  fn = new Function();

1. 介绍
☞ new Function() 构造函数中所有的形参必须都是字符串格式
☞ new Function() 构造函数中,形参个数任意个,但最后一个参数表示函数体
var fn = new Function('a', 'b', 'console.log(123)');
☞ 如果要给函数传递参数,如下写法:
var fn = new Function('a', 'b', 'console.log(a+b)');
fn(1, 2);
//将数字1 传递给形参 a, 数字2传递给形参b

2. 总结:
☞ 所有函数都是 Function() 构造函数的实例
function fn () {}
var fn1 = function () {};
var fn2 = new Function();
console.log(fn instanceof Function);
console.log(fn1 instanceof Function);
console.log(fn2 instanceof Function);

☞ 所有的函数都是一个对象,对象有 __proto__
function fn () {}
var fn1 = function () {};
var fn2 = new Function();
console.dir(fn);
console.dir(fn1);
console.dir(fn2);

☞ 函数之间的三角关系如下图所示:

//代码验证 Function构造函数是否有原型对象
//console.dir(Function);

//代码验证 Function原型对象的构造函数是否指向当前构造函数
//console.log(Function.prototype.constructor);

//代码验证 任何的一个函数是否通过 Function这个构造函数创建的
//var fn = function(){};
//console.log(fn instanceof Function);

//代码验证 函数对象原型是否指向构造函数原型对象
//var fn = function(){};
//console.log(fn.__proto__ === Function.prototype);

1593265036465

# 2. 函数调用方式

# 1.1 普通函数调用

function fn() {} 

fn();  		//函数名调用

fn.call();  //通过call()调用

# 1.2 对象中方法调用

var o = {
    fn: function() {}
}

o.fn()    //对象名.函数名()

# 1.3 构造函数调用

function Start() {}

new Start();  // new 关键字调用函数

# 1.4 事件函数调用

事件源.事件类型 = function() {}    //事件执行时候调用

# 1.5 定时器函数调用

setInterval(function(){}, 500);   // window调用该函数,每隔一段调用一次

# 1.6自调用函数

(function(){})();      //函数自己调用自己

# 3. 函数内部this指向

# 1. 1 总结

  • this的指向,只要调用函数的时候才能确定,一般this的指向都是函数的调用者

  • 普通函数中的this指向window

  • 构造函数中的this指向实例对象

  • 原型对象中方法this指向实例对象

    function Start() {}
    Start.prototype.sing = function() { this ; // 指向当前方法的调用者,实例对象}
    
  • 方法中this指向当前方法调用者

  • 事件对中this指向当前事件源

  • 定时器函数this指向window对象

  • 自调用函数this指向window对象

# 1.2改变函数内部this指向(es5继承)

  • call()

    call() 作用:
    1. 调用函数
    function People() {
    	alert(123);
    }
    //People.call();
    
    2. 改变this指向
    var o = {
    	myage: 123
    }
    function People() {
    	console.log(this); 
    }
    // People.call(o);   //将o对象赋值给this, 现在this就指向新的对象 o,不再是window对象
    
    3. 通过call() 实现属性继承
    function Father(uname) {
    	this.uname = uname;
    }
    function Son(uname) {
        //该构造函数中的this指向当前实例对象,  Father.call()调用的时候,将this 赋值给 Father中的this
    	Father.call(this, uname);
    }
    var zs = new Son('张三');
    console.log(zs);
    
    • apply()
    apply() 作用:
    1. 调用函数
    
    function People() {
       alert(23);
    }
    // People.apply();     与call的用法一样
    
    2. 改变this指向
    var obj = {
    	username: '张三'
    }
    function People() {
    	console.log(this);
    }
    // People.apply(obj);  //调用函数的时候,将obj这个对象赋值给this,所以this现在就执行 obj对象
    
    3. apply()call() 之间的区别 ,后面的参数不一样
    
    // apply() 在传递参数的时候,必须是数组形式的,如下代码所示:
    
    function fn(a, b) {
         console.log(a);
         console.log(b);
         console.log(a + b);
         console.log(this);
    }
    //fn.apply(null, [1, 2]);   虽然实参是数组格式,但是 形参 a 和 b 还是分别得到实参中的值
    //如果不改变函数内部this指向,可以设置 null
    
    
    
    // call() 在传递参数的时候,正常设置就可以,如下代码所示:
    function fn(a, b) {
    	console.log(a);
    	console.log(b);
    
    	console.log(a + b);
    
    	console.log(this);
    }
    // fn.call(null, 1, 2);
    
    
    4.apply() 的应用场景最主要的是操作数组,因为 apply的参数就是一个数组
    // 例如: 通过apply的方式快速求数组中的最大值
    
    ☞先演示; 通过apply调用Math.max()求一组数字的最大值
    //Math.max.apply(null, [1, 2, 3])
    
    ☞在推导演示: 通过apply实现求数组中的最大值
      var ary = [1, 2, 20, 4, 5];
      Math.max.apply(null, ary);
    
    //备注: 当前对象依然要指向Math这个对象,所以建议将 null 改成 Math
    
  • bind()

    1. bind() 不会调用函数,返回一个函数
    //代码演示:
    function fn() {
    	alert(123);
    }
    fn.bind();  // 不会调用函数
    
    //如果要执行该函数,bind的返回值就是一个函数, 执行返回值即可
    //var fn1 = fn.bind();
    //console.log(fn1);
    // fn1()
    
    
    2. bind() 可以改变this指向
    var obj = {
    	uname: '张三'
    }
    function fn() {
    	console.log(this);
    }
    var fn1 = fn.bind(obj);
    fn1();   // 将obj 对象赋值给 this, this指向的就是当前 obj对象
    
    
    3. bind() 可以设置参数,参数的设置方式与 call的方式一样
    
    function fn(a, b) {
    	console.log(a + b);
    }
    (fn.bind(null, 1, 2))();   // 如果不改变对象的指向,那么就设置unll
    
    
    4. bind() 引用: 当不需要调用函数,但又要改变this指向,可以使用bind
    例如: 页面中点击当前按钮禁用,3秒钟后让该按钮启用
    
    var btns = document.querySelectorAll('input');
    	for(var i = 0; i < btns.length; i++) {
    		 btns[i].onclick = function() {
                   //this,指向当前点击按钮
                   this.disabled = true;
    				// 3秒钟种后开启
    				setTimeout(function(){
    					//错误的写法: setTimeout中的this指向的是window对象
    					this.disabled = false; 
    				}, 3000)
    		}
    }
    //分析; 正确写法, 需要将定时器中的this 指向 事件中的this,按钮对象
    //定时器自己就会调用,那么这里只需要改变this指向,又不需要调用函数,所以用bind
    //规律: 要将this改变指向谁,那么就在bind方法中传递谁
    //正确写法如下:
    var btns = document.querySelectorAll('input');
    	for(var i = 0; i < btns.length; i++) {
    	  btns[i].onclick = function() {
    			this.disabled = true;
                // 3秒钟种后开启
                setTimeout(function(){
    				 this.disabled = false; 
                 }.bind(this), 3000)
    		}
    	}
    
    //分析: bind调用setTimeout的时候, 定时器中的this指向window,现在我们需要将window指向外部的按钮对象this,所以在bind中传递 this, 这个this是事件中的this对象
    
    5. bind() 实现tab栏切换
    //必须记住的几个步骤
    1. 先将class 外部的 that 注释掉,代码中的所有 that 都要注意
    2. 在绑定事件的代码中修改代码如下:
    for(var i = 0; i < this.lis.length; i++) {
        //切换要修改
    	this.lis[i].onclick = this.toggleTab.bind(this.lis[i], this);
       
        //删除要修改
    	this.delBtns[i].onclick = this.removeTab.bind(this.delBtns[i],this);
    }
    //添加要修改
    this.addBtn.onclick = this.addTab.bind(this.addBtn, this);
    
    //修改完成后,必须要在对应的方法上设置一个参数,叫that, 例如:
    toggleTab(that) {
    	//可以调用一个公共的功能
    	that.clearClass();
    	//当前li设置类名
    	this.className = 'liactive';
    	that.content[this.index].style.display = 'block';
    }
    

# 1.3 bind,call,apply总结

  • 相同点: 都可以改变this指向
  • 不同点:
    1. call 和 apply 都可以调用函数
    2. bind 不能调用函数
    3. call 和 apply 的参数不一样
    4. call 常用来实现继承
    5. apply 常用来和数组配合使用

# 4.es6严格模式

# 1.0代码模式

HTML结构中有严格模式,松散模式,过渡模式
☞ 不同模式下对代码的规范要求不一样,严格模式最严格,必须按照对应的格式写
https://www.cnblogs.com/shireyhu/p/7825920.html


☞ js设计语法中也包含 严格模式 和 松散模式
☞ 松散模式,代码语法比较宽松,基本上就是想怎么写就怎么写
☞ 严格模式,代码规范比较严格,写出的代码比较正经 

# 1.1 严格模式(ie需要10以上兼容)

  • 消除了js语法中的不合理,不严谨的地方 (例如: 变量不声明也可以使用)
  • 消除代码不安全的问题
  • 禁用了一些保留字作为变量名(例如: class , enum ....)

# 1.3严格模式使用

  • 为整个脚步开启严格模式

    ☞ 在整个js文件的开始位置设置
    'use strict';
    
    ☞ 或者在script标签的开始位置
    <script>
    	'use strict';    
    </script>
    
  • 给函数单独开启严格模式

    function fn() {
        'use strict';
        后面的代码按照严格模式执行
    }
    
    function fn1() {
        按照普通的代码格式执行
    }
    

# 1.4 严格模式的变化

  • 变量的变化

    ☞ 在普通模式下,变量不定义也能赋值
    ☞ 在严格模式下,变量先声明再使用
           'use strict';
    		num = 123;
    		console.log(num);
    ☞ 在严格模式下,变量声明后,变量不能被删除
    	    'use strict';
    		 var num = 123;
    		 console.log(num);
    		 delete num;
    
  • this指向问题

    ☞ 在普通模式下, 函数中的this 指向 window
    ☞ 在严格模式下, 函数中的this 指向 undefined
    ☞ 在严格模式下, 构造函数如果不加 new 调用会报错,因为 this指向的是 undefined
    	    function fn(uname) {
    			this.uname = '张三';
    		}
    		fn();
    ☞ 在严格模式下, 在定时器中的 this 指向的是 window
    ☞ 在严格模式下, 事件,对象的 this 还是指向当前调用者
    
  • 函数变化

    ☞ 在严格模式下, 函数的参数不能重名
    		function fn(a, a) {
    			alert(a + a);
    		}
    		fn(1, 2);
    ☞ 在严格模式下, 函数必须在顶层, 不能在 条件判断中定义函数,循环中定义函数
    if(true) {
    	function fn1() {
    		alert(456);
    	}
    }
    fn1();
    
    
    for(var i = 1; i <= 2; i++) {
    	function fn2() {
    		alert(789);
    	}
    }
    fn2();
    

# 4. 高阶函数

# 1.1 特点
  • 如果函数的参数是一个函数或者返回值是一个返回值: 称为高阶函数

    例如: 定时器,函数作为参数
    
    setInterval(function(){
    }, 2000);
    
    setInterval(fn, 2000);
    function fn() {}
    
    例如:注册事件,函数作为参数
    
    btn.addEventListener('click', function(){});
    btn.addEventListener('click', fn);
    function fn() {}
    
    
    例如: bind方法,返回一个函数
    function fn() {
    	alert(123);
    }
    var fn1 = fn.bind();
    
    //面向对象 + 高阶函数 模拟实现计算器案例  (bind改变this指向)
    

# 5. 闭包

# 1.1 作用域

  • 全局变量

    函数外部定义的变量全局变量
    
  • 局部变量

    函数内部定义的变量, 局部变量特点函数调用完成后,局部变量的值随之消失
    

# 1.2 概念

通俗理解: 闭包, 首先闭包是一种现象; 一个函数能够访问其他函数中的变量

概念:能够访问另一个函数作用域的变量的函数

1593425696885

# 1.3闭包代码演示

//分析如下代码是否有闭包函数
function fn() {
	// 局部变量
	var sum = 123;
	function fn1() {
		//局部变量
		var res = sum + 123;
		return res;
	}
	return fn1;
}
var res = fn();
var s = res();
console.log(s);

// 总结: 在闭包函数中,返回值是一个函数是最常见的一种写法, 所以闭包函数就是一个高价函数

# 1.4作用

1. 延伸了函数内部变量的作用范围
2. 通过闭包访问其他函数中的变量

# 1.4 应用

  • 闭包获取点击按钮索引

    1. 非闭包写法:
    var lis = document.querySelectorAll('li');
    for(var i = 0; i < lis.length; i++) {
        //这里必须要动态给li添加一个索引,才可以访问到,多一行代码,显的啰嗦
        //稍有不慎,同学丢掉这句话就容易报错
        lis[i].index = i;
        lis[i].onclick = function() {
            console.log(this.index);
        }
    }
    
    2. 闭包写法:
    for(var i = 0; i < lis.length; i++) {
    	(function(i){
    		lis[i].onclick = function() {
    			console.log(i);
    		}
    	})(i)
    }
    
  • 3秒之后打印所有元素的内容

    1. 错误的写法,并分析为什么
    // 3秒之后,外层的for循环早已结束, i 变成了4,所以会报错
    var lis = document.querySelectorAll('li');
    for(var i = 0; i < lis.length; i++) {
    		setTimeout(function(){
    		console.log(lis[i].innerHTML);
    	}, 3000)
    }
    
    
    2. 闭包写法解决:
    var lis = document.querySelectorAll('li');
    for(var i = 0; i < lis.length; i++) {
    	(function(i){
    		setTimeout(function(){
    			console.log(lis[i].innerHTML);
    		}, 3000)
    	})(i)
    }
    
  • 计算打车价格案例

    1. 起步加 13 (3公里)
    2. 每多一公里增加 5元  用户输入公里计算总结
    3. 如果遇到拥堵,总价多收10
  • 思考题:

    1. 非闭包案例

      var name = "我是全局变量哦";
      var object = {
         name: '我是局部变量哦',
         getName: function() {
             return function() {
                console.log(this.name);
             }
         }
      }
      object.getName()();
      
    2. 闭包案例

      var name = "我是全局变量";
      var object = {
          name: '我是局部变量',
          getName: function() {
              var that = this;
              return function() {
                  console.log(that.name);
              }
          }
      }
      object.getName()();
      

# 1.5 闭包总结

  • 概念

    闭包: 闭包就是函数的一种现象, 在一个作用域中访问另外一个函数中变量的函数
    
  • 作用

    1. 闭包的存在,可以让局部变量也能访问
    2. 延长了数据的生命周期
    

# 6. 递归

# 1.1 概念

如果一个函数内部调用函数本身就叫递归函数
本质: 函数内部自己调用自己

function fn() {
    fn();
}
fn();

//避免栈溢出,要写结束条件(和循环一样)

# 1.2 案例

  • 求 1 - n 的阶乘

    1. 非递归的版本
    function fn(n) {
    	var sum = 1;
    	for(var i = 1; i <= n; i++) {
    	  sum *= i;
    	}
    	return sum;
    }
    console.log(fn(5));
    
    2. 递归版本
    function fn(n) {
        if(n == 1) {
            return 1;
        }
        return n * fn(n-1);
    }
    fn(2);
    
  • 求斐波那契数列案例

    1, 1, 2, 3, 5, 8, 13, 21 ...   第一项和第二项的和是第三项
    
    求第n个数字的斐波那契数列
    function fn(n) {
        if(n == 1 || n == 2) {
            return 1;
        } 
        return fn(n-1) + fn(n - 2)
    }
    fn(4);
    
  • 根据id遍历对象

    var data = [
        {
            id: 1,
            name: '家电',
            goods: [{
                id: 11,
                name: '冰箱'
            },{
                id:12,
                name: '洗衣机'
            }]
        },{
            id: 2,
            name: '服装'
        }
    ]
    

# 7. 拷贝

# 1.1 浅拷贝

  • 概念

    浅拷贝,拷贝数据一层 或者更深层次的对象只拷贝对象地址的应用
    通俗理解: 将一个数据赋值给另外一个数据,  如果其中一个数据发生改变另外一个也发生改变
    
  • 通过Object.assign(target,sources)实现浅拷贝

    Object.assign(target,sources)
    
    var obj = {
    	uname: '张三',
    	gender: '男',
    	msg: {
    		age: 18
    	}
    }
    var o = {};
    Object.assign(o, obj);
    o.msg.age = 23;
    console.log(o);
    console.log(obj);
    
  • 浅拷贝内部实现的原理

    1593437507536

# 1.2 深拷贝

  • 概念

    将一个数据赋值给另外一个数据, 如果其中一个数据发生改变,另外一个没有改变
    
  • 通过JSON.stringify实现

    var obj = {
       uname: '张三',
       gender: '男',
       msg: {
    	 age: 18
       }
    }
    var o = {};
    var res = JSON.stringify(obj);   // 先将原来的对象转为字符串,字符串是一个简单类型
    o = JSON.parse(res);	 // 然后这个字符串再转化为对象,赋值给 o, 那 o 中就是原来对象中的所有值
    o.msg.age = 23;
    console.log(o);
    console.log(obj);
    
  • 递归方式实现深拷贝

    var obj = {
    			uname: '张三',
    			gender: '男',
    			like : ['吃', '喝'],
    			msg: {
    				age: 18
    			}
    		}
    		var o = {};
    
    		function fn(oldObj, newObj) {
    
    			//遍历原来的对象
    			for(key in oldObj) {
    				var item = oldObj[key];
    				// 如果是数组
    				if(item instanceof Array) {
    					//当前对象属性就是一个数组
    					newObj[key] = [];
    					//将当前数组赋值给对象
    					fn(item, newObj[key]);
    				}else if(item instanceof Object) {
    					//当前对象属性就是一个对象
    					newObj[key] = {};
    					fn(item, newObj[key]);
    				}else {
    					newObj[key] = item;
    				}
    			}
    		}
    
    		fn(obj, o);
    		o.msg.age = 23;
    		console.log(o);
    		console.log(obj);
    
    
    // 拷贝过程中,如果遇到对象,是将对象重复拷贝一份,将最新拷贝的内容复制给新对象
    

    1593440122415

# javaScript高级

# 1.核心知识点

  1. 正则表达式概念
  2. 正则表达式js中的使用
  3. 正则表达式的特殊字符
  4. 正则表达式的替换

# 2.学习目标

  1. 能够说出正则表达式的作用
  2. 能够写出简单的正则表达式
  3. 能够使用正则表达式对表单验证
  4. 能够使用正则表达式替换内容

# 3.开始学习

# 1. 正则表达式

# 1.1概念

正则表达式: 使用单个字符串来描述、匹配一系列符合某个句法规则的字符串

在线验证正则表达式:
https://c.runoob.com/front-end/854

# 1.2作用

  1. 验证表单

    1. 注册信息填写手机号: 
    https://mail.163.com/register/index.htm?from=force/&cmd=register.entrance
    
    2. 注册信息填写身份证号信息:
    https://apply.bjhjyd.gov.cn/apply/user/person/register.html
    
  2. 过滤(替换)页面中的敏感词

  3. 提取(京东搜索演示)

    1593609120791

# 1.3特点

  1. 灵活性,逻辑性强

  2. 可以快速实现匹配字符串的控制

  3. 实际开发中,一般都是复制写好的正则表达式,能够根据需求会修改即可

    ^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$   邮箱正则表达式
    

# 2.正则表达式使用

# 1.1 通过RegExp方式创建

var 自定义正则对象 = new RegExp(/表达式/);
注意:
1. 正则是一种检索或者过滤条件的语法
2. 使用的是 //,不能使用其他斜杠

# 1.2通过字面量创建正则表达式

var reg =  / /;
注意:
1. 不需要加引号

# 1.3检测正则表达式

正则表达式.test(str);

备注:
test()正则表达式中一个方法,检测是否匹配正则规则,返回布尔类型的结果,true 表示符合 false 表示不符合

例如:
var reg = /123/;
reg.test('123');

# 3. 正则表达式字符

# 1.0正则表达式基本语法介绍

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

简单模式:
var reg = /abc/;

解释: 表示一个值中只要包含连续三个值是abc就可以
例如:
var reg = /abc/;
var str = '123abc123';
console.log(reg.test(str));  //true

var str = 'abc123';
console.log(reg.test(str));  //true

var str = '123abc';
console.log(reg.test(str));  //true

var str = '1a2b3c';
console.log(reg.test(str));  //false

var str = 'aabbcc';
console.log(reg.test(str));   //fasle

# 1.1 边界符^$

  • ^符号
^: 匹配输入的开始,以谁开始 (精确匹配)
例如:
//规则: 以小写字母a开始就可以
var reg = /^a/;
var str = 'Abc';
console.log(reg.test(str));  //false

var str = 'Aabc';
console.log(reg.test(str));  //false

var str = 'abc';
console.log(reg.test(str)); //true

var str = 'aabc';
console.log(reg.test(str));  //true

var str = 'a';
console.log(reg.test(str));  //true


思考:举一反三

//规则: 以数字1开始就可以
var reg = '^1';
var str = 123;
console.log(reg.test(str));  //true

var str = 2123;
console.log(reg.test(str));  //false

//规则: 以字母abc开始就可以
var reg = '^abc';
var str = 'abc';
console.log(reg.test(str));  //true

var str = 'a123b123c';
console.log(reg.test(str));  //false

var str = 'aabbcc';
console.log(reg.test(str));  //false

var str = 'abc123abc';
console.log(reg.test(str));  //true
  • $

    $: 匹配输入的结束
    
    例如:
    // 规则:  匹配以小写字母a结束
    var reg = /a$/;
    var str = 'apple';
    console.log(reg.test(str));  //false
    
    var str = 'appleA';
    console.log(reg.test(str));  //false
    
    var str = 'appleaa';
    console.log(reg.test(str));  //true
    
    var str = 'a';
    console.log(reg.test(str));  //true
    
    
    //举一反三:
    var reg = /中国$/;
    var str = '我爱你中国';
    console.log(reg.test(str));  //true
    
    var str = '中国我爱你中国';
    console.log(reg.test(str));  //true
    
    var str = '中国我爱你';
    console.log(reg.test(str));  //false
    
    var str = '中我爱你国';
    console.log(reg.test(str));  //false
    
  • 思考题:

    //规则: 以小写字母a开始,以小写字母a结束	,那就是字母a,不能有其他的
    var reg = /^a$/;
    
    var str = 'a';
    console.log(reg.test(str));  //true
    
    var str = 'aa';
    console.log(reg.test(str));  //false
    
    
    
    //规则: 以abc开始以abc结束,就是abc不能有其他的
    var reg = /^abc$/;
    var str = 'abc';
    console.log(reg.test(str)); //true
    
    
    var str = 'aabbcc';
    console.log(reg.test(str)); //false
    
    
    var str = '123abc123';
    console.log(reg.test(str)); //false
    
    
    //精确适配以上都是
    

# 1.2字符类[]

  • []

    []: 一个字符集合。匹配方括号中的任意字符
    
    例如:
    //规则: 匹配 abcd中任意一个字符
    var reg = /[abcd]/;
    var str = 'a';
    console.log(reg.test(str)); //true
    
    var str = 'b';
    console.log(reg.test(str)); //true
    
    var str = 'ac';
    console.log(reg.test(str));  //true
    
    var str = 'bfc';
    console.log(reg.test(str));  //true
    
    var str = 'efg';
    console.log(reg.test(str));  //false
    
    var str = 'Abc';
    console.log(reg.test(str));  //true
    
    var str = 'A';
    console.log(reg.test(str));  //false
    
  • 思考题:

    //规则: 适配以a,b,c三个中任意一个开始
    var reg = /^[abc]/;
    var str = 'a';
    console.log(reg.test(str));  //true
    
    
    var str = 'buffa';
    console.log(reg.test(str));  //true
    
    var reg = /^[abc]/;
    var str = 'cccc';
    console.log(reg.test(str));  //true
    
    
    
    //规则: 必须是 a, b, c 三个字母中的一个,有其他的不行, 三选一
    var reg = /^[abc]$/;
    var str = 'cccc';
    console.log(reg.test(str)); //false
    
    
    var str = 'a';
    console.log(reg.test(str)); //true
    
    var str = 'ab';
    console.log(reg.test(str));  //false
    
  • [-]

    [-]: 表示范围
     
    例如:
    //规则: 匹配 a 到 f 之间的任意一个
    var reg = /[a-f]/;
    var str = 'abcdef';
    console.log(reg.test(str));  //true
    
    var str = 'ag';
    console.log(reg.test(str)); //true
    
    var str = 'g';
    console.log(reg.test(str));  //false
    
    
    
    举一反三:
    //规则: 以 a - f 中任意一个字母开头
    var reg = /^[a-f]/;
    var str = 'ga';
    console.log(reg.test(str)); //false
    
    var str = 'afa';
    console.log(reg.test(str));  //true
    
    var str = 'efg';
    console.log(reg.test(str));  //true
    
    
    //规则: 必须是 a - f 中的任意一个字母, 多选一
    var reg = /^[a-f]$/;
    var str = 'efg';
    console.log(reg.test(str)); //false
    
    var str = 'af';
    console.log(reg.test(str));  //false
    
    var str = 'c';
    console.log(reg.test(str));  //true
    
  • 课堂案例

    1. 用户名中包含 a到z 或者 A 到Z 正则如何写? (用户名中必须有一个是字母)

      var reg = /[a-zA-Z]/
      
    2. 用户名中只能包含 a到z 或者 A到Z 中的一个字母 (用户名必须是一个字母)

      var reg = /^[a-zA-Z]$/
      
    3. 用户名中包含 a到z 或者 A 到Z 或者 0 - 9 (用户名中至少包含一个字母或者一个数字)

      var reg = /[a-zA-Z0-9]/
      
  • ^取反

    //规则: 匹配只要不是 abc这三个字母中的任何一个都可以
    var reg = /[^abc]/;
    var str = 'a';
    console.log(reg.test(str));  //false
    
    var str = 'd';
    console.log(reg.test(str));  //true
    
    var str = 'def';
    console.log(reg.test(str));  //true
    
    
    举一反三:
    //规则: 只要不是以 a b c 三个字母中任意一个字母开头就可以
    var reg = /^[^abc]/;
    var str = 'def';
    console.log(reg.test(str));  //true
    
    var str = 'abc';
    console.log(reg.test(str));   //false
    
    var str = 'cawq';
    console.log(reg.test(str));  //false
    

# 1.3量词符

  • *

    出现次数 >= 0
    
    //匹配: 字母a出现次数 >= 0     备注:要出现只能出现a,不能有其他的字符
    var  reg = /^a*$/;
    var str = 'aaa';
    console.log(reg.test(str));   //true
    
    
    var str = '';
    console.log(reg.test(str));   //true
    
    
    var str = 'bcf';
    console.log(reg.test(str));  //false
    
    
    举一反三:
    
    //匹配: a - z 中任意一个字母出现次数 >=0次
    var  reg = /^[a-z]*$/;
    var str = '123abcdef123';
    console.log(reg.test(str));  //false
    
    var str = '';
    console.log(reg.test(str));  //true
    
    var str = 'aabbcc';
    console.log(reg.test(str));  //true
    
  • +

    出现次数 >= 1
    
    //匹配: 字母a出现次数 >= 1    要出现只能出现字母a
    var  reg = /^a+$/;
    var str = 'bcf';
    console.log(reg.test(str));   //false
    
    var str = '';
    console.log(reg.test(str));   //false
    
    var str = 'aabb';
    console.log(reg.test(str)); //false
    
    var str = 'aa';
    console.log(reg.test(str));  //true
    
    
    
    举一反三:
    //匹配: a - z 中任意一个字母出现次数最少一次
    var reg = /^[a-z]+$/;
    var str = '';
    console.log(reg.test(str));  //false
    
    
    var str = 'abcdef';
    console.log(reg.test(str));  //true
    
    
    var str = 'aabbccddeeff';
    console.log(reg.test(str));  //true
    
    
    var str = '123abcdef123';
    console.log(reg.test(str));  //false
    
  • ?

    出现次数 1次 或者 0// 匹配: 字母a出现1次或者0次
    var reg = /^a?$/;
    var str = 'aa';
    console.log(reg.test(str));   //false
    
    var str = 'a';
    console.log(reg.test(str));  //true
    
    var str = '';
    console.log(reg.test(str));  //true
    
    
    
    举一反三:
    
    // 匹配:a - z 之间任何一个字母可以出现一次 或者 0次
    var reg = /^[a-z]?$/;
    var str = 'aa';
    console.log(reg.test(str));  //false
    
    var str = 'a';
    console.log(reg.test(str));  //true
    
    var str = '';
    console.log(reg.test(str));  //true
    
  • {n}

    出现具体n次
    
    //匹配: 字母a必须出现3次
    var reg = /^a{3}$/;
    var str = 'a';
    console.log(reg.test(str)); //false
    
    var str = 'aa';
    console.log(reg.test(str)); //false
    
    var str = 'aaa';
    console.log(reg.test(str)); // true
    
    var str = '';
    console.log(reg.test(str)); //false
    
    var str = 'bdc';
    console.log(reg.test(str)); //false
    
  • {n,}

    出现大于等于 n 次
    
    // 匹配: 字母a出现的次数大于等于3
    var reg = /^a{3,}$/;
    var str = 'a';
    console.log(reg.test(str)); //false
    
    var str = 'aa';
    console.log(reg.test(str)); //false
    
    var str = 'aaa';
    console.log(reg.test(str)); // true
    
    var str = 'aaaaaa';
    console.log(reg.test(str)); // true
    
  • {n,m}

    出现次数大于等于 n 小于等于m
    // 匹配: 字母a出现的次数大于等于3小于等于4次
    var reg = /^a{2,4}$/;
    var str = 'aaa';
    console.log(reg.test(str)); //true
    
    
    var str = 'aaaa';
    console.log(reg.test(str)); //true
    
    
    var str = 'aa';
    console.log(reg.test(str)); //true
    
    var str = 'aaadc';
    console.log(reg.test(str));  //false
    
    var str = '';
    console.log(reg.test(str));  //false
    
    注意: 数字之间不能出现空格
    
    
    
    举一反三:
    //匹配: 字母 a-z 出现总次数等于4   (从字面a-z中只能选4个值)
    var reg = /^[a-z]{4}$/;
    var str = 'aaaa';
    console.log(reg.test(str));  //true
    
    var str = 'aaaabbbb';
    console.log(reg.test(str));  //false
    
    var str = 'abcdffff';
    console.log(reg.test(str));  //false
    
    var str = 'hjkl';
    console.log(reg.test(str));  //true
    
    
    //匹配: 字母 a - z 出现总次数大于等于4   (从字面a-z中最少选出4个)
    var reg = /^[a-z]{4,}$/;
    var str = 'aaa';
    console.log(reg.test(str));  //false
    
    var str = 'aaaabbbb';
    console.log(reg.test(str));  //true
    
    var str = 'abcdffff';
    console.log(reg.test(str));  //true
    
    var str = 'hjkl';
    console.log(reg.test(str));  //true
    
    
    //匹配: 字母 a -z 出现次总次数 大于等于2 小于等于5  (从字面a-z中最少选出2个到5个)
    var reg = /^[a-z]{2,5}$/;
    var str = 'aaa';
    console.log(reg.test(str)); //true
    		  
    var str = 'aaaabbbb';
    console.log(reg.test(str)); //false
    
    var str = 'hjkl';
    console.log(reg.test(str)); //true
    

# 4. 用户名案例

# 1.1 呈现要求

  • 如果用户名输入合法,则提示信息为:用户名合法,颜色为绿色
  • 如果用户名输入不合法,提示用户名不符合规范,颜色为红色

# 1.2用户名输入要求

  • 用户名只能为英文字母或者数字或下划线组成,且用户名长度为6-16位

# 1.3具体实现

// 要求:用户名只能为英文字母,数字,下划线组成,且用户名长度为6-16位
var reg = /^[a-zA-Z0-9_]{6,16}$/;
//获取当前输入框
var int = document.querySelector('input');
int.onfocus = function() {
	this.value = '';
}
int.onblur = function() {
	var v = this.value;
	if(reg.test(v)) {
	 	this.style.borderColor = 'green';
	 }else {
	 	this.style.borderColor = 'red';
	 	this.value = '输入有误';
	 }
}

# 5.预定义类

备注: 使用在线工具演示即可

# 1.1 数字类

\d :  匹配0-9之间的任意一个数字  相当于 [0-9]

\D:   匹配0-9以外的所有字符      相当于[^0-9]

# 1.2 字符类

\w :  匹配任意的字母,数字和下划线  相当于 [a-zA-Z0-9_]
\W :  除所有的字母,数字,和下划线以外的字符  相当于 [^a-zA-Z0-9_]

# 1.3特殊符号

\s: 匹配空格(包含换行符,制表符,空格符等)    相当于[\t\r\n\v\f]
\S: 匹配除特殊字符外      相当于[^\t\r\n\v\f]

备注: \r 表示回车   \n 表示换行  \v 垂直制表符   \f换页符

# 1.4课堂案例

☞ 补充:

| 表示或者的意思
例如:
var reg = /a|b/;    
//匹配字串中只要包含字母a 或者 b 即可

var reg = /^a|b$/;  
//匹配字符串中以a开始或者以b结束

var reg = /^[a,b]|[c,d]$/;  
//匹配字符串中以a或者b开头,或者 以 c或者d结束

var reg = /^[a,b]{2}|[1,2]{3}$/;  
//匹配字符串中以字母a或者b开始数量为2个 或者 以数字 1 或者2结束. 数量为3个

var reg = /\d{3}-\d{8}|\d{4}-\d{7}/;
//匹配一个三位数-8位数  或者  一个四位数-7位数



☞ 补充:
() 表示一个整体
var reg = /^foot{2}$/;     //匹配字符串中字母t出现2次

var reg = /^(foot){2}$/;   //匹配整个单词foot出现2次


//参考答案:
1. 座机正则  010-123456780313-1234567
var reg = /^([0-9]{3}-[0-9]{8}|[0-9]{4}-[0-9]{7})$/;
var reg = /^(\d{3}-\d{8}|\d{4}-\d{7})$/;

非严谨写法:
var reg = /^(\d{3,4}-\d{7,8})$/;


2.手机号正则: 152-11111111  或者 131-11111111
var reg = /^1[3578][0-9]{9}$/


3. QQ正则(1开始后面最少5位数字最多8位数字)
var reg = /^[1-9][0-9]{5,8}$/;


4. 短信验证码正则(6位数字)
var reg = /^[0-9]{6}$/;

5. 登录密码(字母a-zA-Z0-9_)(最少6)
var reg = /^[\w\d]{6,}$/;

# 6.正则替换

# 1.1语法

正则对象.replace(当前值,替换为哪个值);

# 1.2正则参数

//[switch]

g : 全局匹配
i: 忽略大小写
gi: 全局忽略大小写

例如: 
var str = 'a中国';
var newstr = str.replace(/a/, '爱');
 


课堂案例:
var tar = document.querySelector('textarea');
var btn = document.querySelector('input');
var div = document.querySelector('div');
btn.onclick = function() {

	var v = tar.value;

	var reg = /激情|日本|川建国/g;

	v = v.replace(reg, '我爱中国');

	div.innerHTML = v;
}