js 之 ES3 学习内容(一)js基础、11个引用对象类型及API

450 阅读28分钟

javascript由三部分组成

1、**ECMAScript**(ES3/5/6) - 核心语法:前三天学习的内容 - 内功心法(以后可能做任何操作都离不开他们)
2、DOM - Document Object Model:文档 对象 模型 - 操作文档(HTML) - 外功招式(特效)
3、BOM - Browser Object Model:浏览器 对象 模型 - 操作浏览器 - 有重点但是用处不大

一、js概述

1、含义:js是运行在js解释器中的解释型、弱类型、面向对象的脚本语言

(一)、js解释器

           是浏览器自带的不需要安装的,在node.js服务器端,需要安装一个独立的js解释器
           (浏览器能识别html和css是因为浏览器也自带html和css解释器)

(二)、js的特点

           解释型:不需要检查对错,直接运行,遇到错误就停止执行;(强类型:先检查对错,一旦错误直接就不运行)
           弱类型:数据决定数据类型(强类型:由数据类型决定了程序员想要保存的数据是什么)
           面向对象语言:万物皆对象(包含属性和方法)===>语法:对象名.属性名/对象名.方法名()

(三)、js的用途

           css可以做的js可以做,css不可以做的js也可以做;可以用js中的ajax方法实现前端与服务器端的交互

(四)、js的使用方式(2种)

           外部引入script文件====><script src="js文件路径"></script>
           直接在html中书写<script >js代码</script>
           使用外部引入方式最好:原因==>就算js执行耗时,还可以先看到html和css效果;js会操作html结构,如果js放在head中可能会找不到运算

(五)、js代码注释

        作用: 提示自己/别人  2、玩 - 养成一个写注释的好习惯
        单行注释://
        多行注释:/**/

(六)、js输出方式:

         控制台F12.输出方式:console.log()//强推,不会影响用户体验感
         页面输出方式:documet.write();//绑定事件会替换页面已有内容
         弹出框输出方式:alert();//会卡住页面,需要关闭弹出框后才可能看到页面的内容

(七)、变量

        语法:var 变量名=变量值
        特殊点:取名要见名知意;
               不能以数字开头
               连创多个变量可以省略后面的var并把;变为,    
               =是赋值符号,是把右边的变量值赋值给左边(可以用创建/定义/声明)

(八)、常量

        语法:const 常量名=常量值

二、数据类型 (5种基本类型和11种对象类型)

1)原始类型/值类型/基本类型: 数字,字符串,布尔值,UndefinedNull
    
                                   Undefined无意义,默认值只有一个,就是undefinedNull只有一个,就是null,用来释放变量/内存;
      (2)引用类型

三、算数运算和表达式

(一)、算数运算

       + - * / % 
                 具有隐式转换,默认会转为数字再运算;
           (1)+运算中,遇到字符串会变成拼接,不再是+运算;
           (2)在-*/ % 中,字符串也可以进行运算,但前提是纯数字字符串,如果是非纯数字字符串,则结果为NAN;
           (3)xx转为数字的话,会转为什么结果
                                           true->1
				           false->0
				           undefined->NaN
				 null->0
				"100"->100
				"100px"->NaN
            NANNot A Number 不是一个数字,但确实是一个number类型(不是一个有效的数字)
    
            NAN的缺点:NAN参与任何算术运算,结果仍为NANNAN参与任何比较/关系运算,结果都是false,甚至不认识自己
    
           解决方案:!isNAN(x),判断是不是有效数字;
           作用:防止用户恶意输入
           结果:true是一个有效数字,false就是NAN

(二)、比较/关系运算符:> < >= <= == != === !==

返回的结果都为布尔值:truefalse
	往往比较运算符出现在分支、循环的判断条件之中
	   隐式转换:默认,左右两边都会悄悄的转为数字,再比较
	       特殊:
			1、如果左右【两边】参与比较的都是字符串,按位PK每个字符的十六进制的unicode号(十进制ascii码)
					0-9<A-Z<a-z<汉字
					百度搜索:中文字符集 Unicode 编码范围
					汉字的第一个字:一(unicode:4E00)-(ascii:19968)
					汉字的最后一个字:龥(unicode:9fa5)-(ascii:408692NaN参与任何比较运算结果都为false,带了一个问题,没有办法使用普通的比较运算判断一个xx是不是NaN
				解决:!isNaN(xx); true->有效数字  false->NaN

			3undefined==null,
			   问题:==区分不开undefinednull,怎么才能区分开?
			   undefined===null
			   全等:===,要求值和数据类型都要相同,就是不再带有隐式转换
			         !==,不带隐式转换的不等比较
				写一个String()方法的底层原理
					function String(x){
						if(x===null){
							return "null";
						}else if(x===undefined){
							return "undefined";
						}else{
							return x.toString();//return 返回
						}
					}
                                        var result=String(x);
                                        console.log(result);

(三)、逻辑运算符

悄悄变为布尔,然后比较得出结论
	&&:与(并且)
	    只有全部条件都为true,最后结果才为true
	    只要有一个条件为false,最后结果就为false

	||:或
	    只有全部条件都为false,最后结果才为false
	    只要有一个条件为true,最后结果就为true

	 !:颠倒布尔值:
		!true -> false
		!false -> true
                
    特殊用法:短路逻辑
          ***短路逻辑:只要前一个条件已经可以得出最后结论,则后续条件不再执行!
			&&短路:如果前一个条件为true,后一个操作才执行
				如果前一个条件为false,后一个操作不执行
				简化了【简单的】分支:if(条件){操作}
				语法:条件&&(操作);
				举例:曾经:if(total>=500){total*=0.8};
				      现在:total>=500&&(total*=0.8);
     特殊:1、【简单的】-> 操作只能有一句话!
				建议:能用短路分支绝对不用if分支

			||短路:如果前一个条件为true,不需要执行后一个
				如果前一个条件为false,需要执行后一个
				使用场景:两个值二选一 - 后期做浏览器兼容性(老IE)
                                btn.onclick=function(e){//event事件对象 |                                          e=e||window.event;//e->undefined |
                                 console.log(e.screenX);            |
                                 }

(四)、位运算


	   左移:m<<n,读作m左移了n位,相当于m*2的n次方
	   右移:m>>n,读作m右移了n位,相当于m/2的n次方
	   缺点:底数永远只能是2       

(五)、赋值运算

 一句话执行了两个操作,先运算,再赋值
		+= -= *= /= %= ++ --
		何时使用:只要取出变量中的值,在做计算,之后还要在保存回去时,就要试用赋值运算
		i=i+1;//比较low的写法
		推荐:
		  i+=1 或者 i++;

		递增:i++; 每次只能+1
		累加:i+=n; 每次想加几,随便你

		***鄙视时:++ 分为 ++i 和 i++
			1、单独使用时,没有参与别的表达式,放前放后都一样
			2、如果参与了别的表达式:
				变量中的值其实都会+1
				++i,返回的是递增后的【新值】
				i++,返回的是递增前的【旧值】         
	         案例:  var i=1;
			console.log(i++);//1
			console.log(i);//2

			var me=1;//5 
			var sn= me++ + ++me + me++ + ++me;
					// 1	   3	  3		 5
			console.log(n);//
			console.log(i);//

			var me=1;//8
			var sn= me++ + me++ + ++me + ++me + me++ + ++me + me++;
			//		1	   2      4      5		5      7      7
			console.log(sn);//31
			console.log(me);//8

(六)、三目运算:简化分支:if(){}else{} if(){}else if()else{}

语法:
		  1、条件?操作1:默认操作;

		  2、条件1?操作1:条件2?操作2:默认操作;

		问题:1、只能简化简单的分支 - 三目操作也只能有一句话,不怕,很多情况操作就只有一句话
		      2、默认操作不能省略 - 哪怕条件都不满足,至少还要做一件事
		
		总结:
			if(){}			==		&&短路
			if(){}else		==		三目
			if(){}else if(){}else	==		三目

			能用三目/短路尽量不用if分支
                        
           案例:var age=17;
			if(age>=18){console.log("去网吧")}else{console.log("玩泥巴")};
			console.log(age>=18?"去网吧":"玩泥巴");
			
			var score=50;
			if(score>=90){console.log("兰博基尼")}else if(score>=60){console.log("奥迪双钻")}else{console.log("钉子皮托")}
			console.log(score>=90?"兰博基尼":score>=60?"奥迪双钻":"钉子皮托");  

扩展:

    用户输入框: var userprompt("提示文字","默认值")===>默认值可省略
    查看数据类型:typeof()

四、数据类型转换

(一)、隐式转换(多半都在运算符之中)

      1)、算数运算符:+-*/%      具有隐式转换:默认两边都转为数字,再运算
                                 特殊点:见昨天的内容
      2)、比较运算符
      3)、逻辑运算符

(二)、显示/强制转换

              隐式转换出来的结果不是我们想要的,程序员可以手动调用一些方法进行数据类型的转换

【1】、转字符串:2种

                   xx.toString();不执行undefinednull不能用.点操作
     
                   String(xx),万能的,完全等效于隐式转换,还不如直接+"".

【2】、转数字:3种

(1)、parseInt:转整数,只能是str和num


       执行原理:从左向右依次读取每一个字符,碰到非数字字符则停止,并不认识小数点,如果一来不认识则为NANconsole.log(parseInt(35.45));//35 
               console.log(parseInt("35px"));//35
               console.log(parseInt("3hello5"));//3
               console.log(parseInt("35.45"));//35	
               console.log(parseInt("hello35"));//NaN
	       console.log(parseInt(true));//NaN

(2)parseFloat:转小数,只能是str

      执行原理:几乎和parseInt一直,但是认识第一个小数点
			console.log(parseFloat("35.45"));//35.45
			console.log(parseFloat("35.45.45"));//35.45
			console.log(parseFloat("35.4abc5.45"));//35.4
			console.log(parseFloat("35.45px"));//35.45
       以上两个API作用:强制转为数字的同时并且去掉单位

(3)Number(x);

           万能的,任何人都可以转为数字,完全等效与隐式转换,还不如*1 /1 -0     

【3】转布尔:1种

        Boolean;万能的,任何人都可以转为一个布尔值
         只有6个会转为false0    ""    false   undefined  null NaN,其余的都为true
        
        注意:在【循环或分支的条件判断】中都会带有此隐式转换,隐式转换为布尔值
				if(Boolean(条件)){}
                                
         例子:(只要判断条件不为false,都会执行操作)   
         
                      if(undefined){//条件其实不管些什么最后都会变成一个布尔值,悄悄的我们看不见的套上了一个Boolean方法
				console.log("啦啦啦啦啦");
			}

			if([1,2,3,4,5]){
				console.log("啦啦啦啦啦");
			}

			if(function f1(){}){
				console.log("啦啦啦啦啦3");
			}

			if(Array.prototype.indexOf){//函数->true undefined->false
				document.write("您的浏览器支持此方法")
			}else{
				document.write("您的浏览器是个垃圾")
			}

五、分支结构

(一)、程序的结构:3种

	1、顺序结构:默认结构,从上向下依次执行每一句代码
	2、分支结构:根据判断条件,选择一部分代码去执行(只会走一条路)
	3、循环结构:根据判断条件,选择是否重复执行某一段代码

(二)、分支结构

if分支:

        1、一个条件一件事,满足就做,不满足就不做
		if(判断条件){
			操作
		}
                
            短路逻辑:if条件&&(操作)
            
	2、一个条件两件事,满足就做第一件,不满足就做第二件
		if(判断条件){
			操作1
		}else{
			默认操作
		}
           三目运算:条件1?操作:默认操作;

	3、多个条件多件事,满足谁就做谁
		if(条件1){
			操作1
		}else if(条件2){
			操作2
		}else if(条件3){
			操作3
		}else{
			默认操作
		}

             三目运算:条件1?操作:条件2?操作2:默认操作;
		强调:
		  1、最后的else是可以省略的,但如果条件都不满足,则分支白写
		  2、条件有时候写的顺序需要注意,因为分支只会走一条路

switch....case分支

        switch(变量/表达式){
		case1:
		操作1;
		case2:
		操作2;
		default:
		默认操作;
	}
	
	特殊:1case的比较不带隐式转换
	      2、问题:默认只要一个case满足后,会将后面所有的操作全部做完
		 解决:break;
		 建议:每一个case的操作后都跟上一个break
			有的地方也可以不加break:
				1、最后的一个操作default可以省略break;
				2、如果中间多个条件,做的操作是一样的,也不需要
	      3default可以省略,如果条件都不满足的情况,什么事都不会做

	面试题:if  vs  switch
	  1switch...case... 缺点:必须要知道准备的结果才能使用,不能做范围判断
			      好处:执行效率相对较高

	  2if...else 缺点:执行效率相对较低
		       优点:可以是范围判断

	  建议:代码优化,尽量将所有的 if...else 换成 switch...case、三目
          
   案例
   		   
![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bbf67f55551941629148d6f21fac9ee0~tplv-k3u1fbpfcp-watermark.image?)
                   
                var user=parseInt(prompt("欢迎使用10086查询系统,\n1、查询话费 \n2、查询流量 \n3、查询套餐 \n4、人工服务"));
			switch(user){
				case 1:
				alert("正在查询话费...");
				break;
				case 2:		
				case 3:
				alert("正在维护中...");
				break;
				case 4:
				alert("正在转接人工...");
				break;
				default:
				alert("暂无此功能");
			}
     
                        

(三)、循环结构

循环三要素:循环变量;循环条件;循环变量的变化

1、while循环

            var 循环变量=几;
	    while(循环条件){
		             循环体;
		             循环变量变化一下;
	    }

1、执行原理:
                  先判断循环条件,如果条件为真,则执行【一次】循环体
		  然后再判断循环条件,如果条件为真,则执行再【一次】循环体
		  ...
		  直到条件为假,循环才会结束
	注意:循环是一次一次执行的 - 并不是同事执行的,只不过计算机的CPU计算速度比较快

2、死循环:
              永远不会结束的循环 - 保存死循环也是可以退出循环
	      何时:不确定要执行的具体次数,但往往死循环会搭配上break进行退出
		               while(true){
			                   循环体
		               }

	      流程控制语句:
                          循环退出语句:break; - 出现循环之中
                                       continue-退出当前次循环,会继续执行后面的操作

do.... while循环

          var 变量=几;
          do(循环体){
                         操作
                         循环变量变化
          }while(循环条件)

while和do...while的区别

       只看第一次:如果第一次大家都满足条件,则没有区别;
                  如果第一次大家都不满足条件,while一次都不会执行,do...while会执行一次操作。
                  

2、for循环:

                            while能做,for也能做,而且for看上更加的简单/简洁

         for(var 循环变量=几;循环条件;变量的变化){
			循环体
	       }

         特殊:
	       1、死循环:for(;;){循环体} - 两个分号一个不能少

	       2、循环变量处其实可以创建多个变量

	       3、也支持break

	总结:
	      1while:一般都不用,何时使用:不确定循环次数的时候
	      2for:常用,确定循环次数时

六、函数

[一]、自定义函数

function,也叫方法

(一)、定义:

             函数是一个被预定义好的,可以反复使用的代码,是个独立的功能体,可以将若干代码放在里面。

(二)、创建函数

(1)、声明方式

            function 函数名(形参,.....){
                        函数体;
                        return 返回值/结果
            }

(2)、直接量方式

            var 函数名=function(形参,.....){
                        函数体;
                        return 返回值/结果
            }
           
           return本意是指退出函数,但如果后面跟着一个数据,顺便将数据返回到函数作用域的外部,但return只负责返回结果不负责保存,需要自己去创建一个变量接住结果;
           return只能写一次,并且最好是写在函数体的最后;同时return可以省略,会返回默认值undefined

(2)、构造函数方式

              var 函数名=new Function("形参1","形参2",...,"函数体;return 返回值;");
               何时使用:如果你的函数体不是固定的而是动态拼接的一个字符串
        比如:以下案例,我们让用户来参与到底是顺序还是降序排列
			var arr=[321,5,43,65,8,213,43,765,12];
			var user=prompt("排序数组,如果您输入a-b则为升序排列,如果您输入b-a则为降序排列");//"a-b" "b-a"
			var compare=new Function("a","b","return "+user);
			arr.sort(compare);
			console.log(arr);

(三)、调用

              var 接住返回的结果=函数名(实参,...);
              绑定在页面元素之上,用户来触发了:<elem onclick="js语法">
              函数名(实参)//调用几次执行几次</elem>

(四)、什么东西适合放在函数之中?(何时使用)

 1、不希望打开页面立刻执行,等用户来触发
 2、不希望只执行一次,可以反复触发
 3、希望绑定在页面元素之上时
 4、建议:以后每一个作业都要封装为一个函数:函数在js中具有第一等公民的地位,函数中的变量都是会自动释放的

(五)、带有参数的函数:

       榨汁机 - function:功能:榨汁
		原材料 - 参数:放入一个苹果、梨子、香蕉...
		如何创建带参数的函数:
			function 函数名(形参,...){//形参(形式参数)其实就是一个变量,只不过不需要var
				函数体;
			}

		调用带参数的函数的时候:
			函数名(实参);//实际参数

			function zzj(fruit){
				console.log("榨"+fruit+"汁");
			}
			
			zzj("苹果");
			zzj("梨子");

		强调:
		  1、传入实参的顺序一定要和形参的顺序对应上
		  2、函数不一定非要有参数:1、如果你的操作是固定的,则不需要传递参数
				          2、根据传入的实参不同,做的操作略微不同,则需要添加参数

(六)、作用域

(1)全局作用域:

                  全局变量和全局函数,在页面的任何地方都可使用

(2)函数/局部作用域:

                 局部变量和局部函数,在当前函数调用时内部可以

(3)变量的使用规则:

                    优先使用自己的,自己没有找全局,全局没有则报错
            特殊点:
                   a、不能对着一个没有声明的变量直接赋值,会导致全局污染
                   b、局部可以使用全局的,但全局无法使用局本的。解决:return
                   
            案例:
               1function f1(){
				a=100;//全局污染 - 直接在全局创建了一个全局没有的变量
				console.log(a);//100
			}
			f1();
			console.log(a);//100
                        
               2var a;
			function f1(){
				a=100;//修改全局
				console.log(a);//100
			}
			f1();
			console.log(a);//100
                        
                        
                 3function f1(){
				var a=100;
				console.log(a);//100
			}
			f1();
			console.log(a);//报错,全局使用不到局部的

(七)、声明提前(即:先使用后创建)

           在程序正式执行之前;
           会悄悄地将var声明的变量和function声明的函数,全部集中提前到当前作用域的顶部;
           但是赋值会留在原地;
           变量比函数轻;
       
         (函数中,声明方式创建的函数具有完整的声明提前,直接量方式具有部分声明提前,赋值留在原地)
      
      案例:
      
      // 鄙视题1
			var a=10;
			function f1(){
				console.log(a);//undefinde
				var a=20;
				console.log(a);//20
			}
			f1();
			console.log(a);//10

     // 鄙视题2:
			function fn(){
				console.log(1);//2
			}
			fn();
			function fn(){
				console.log(2);//2
			}
			fn();
			var fn=100;//报错
			fn();

	// 鄙视题3:
			function fn(){
				console.log(1);//1
			}
			fn();
			var fn=100;
			var fn=function(){
				console.log(2)//2
			}
			fn();

	// 鄙视题4:
			var fn=function(){
				console.log(1)//1
			}
			fn();//
			function fn(){
				console.log(2);//1
			}
			fn();//
			var fn=100;
			function fn(){
				console.log(3);//报错
			}
			fn();

	// 鄙视题5:
			function fn(){
				console.log(1);
			}
			fn();//3
			var fn=function(){
				console.log(2);
			}
			function fn(){
				console.log(3);
			}
			var fn=function(){
				console.log(4);
			}
			fn();//4

	//鄙视题6:
			function f1(){
				console.log(a);//10
				a=20;
				console.log(a);//20
			}
			console.log(a);//undefined
			var a=10;
			f1();
			console.log(a);//20
			a=100;
			console.log(a);//100

(八)、按值传递:两个变量之间进行赋值:

如果传递的是原始类型的值:
			修改一个变量,另一个变量是不会受到影响的,其实是复制了一个【副本】给对方

		如果传递的是引用类型的对象:
			修改一个变量,另一个变量是会受到影响的,因为大家操作其实是同一个【地址值】

(九)、重载

         相同的函数名,传入不同的实参,可以自动选择对应的函数执行操作
         
         为什么:减轻程序员的负担!
		      问题:js的语法不支持重载!
			    js不允许多个同名函数同时存在,如果同时存在,最后的会覆盖之前的所有
		      解决:在【函数中】有一个对象 - arguments对象
			    什么是arguments:只能在函数中使用,自动创建,是一个类数组对象(类似数组)
				       作用:***可以接收所有传入的实参
			    argument对象是一个类数组对象:长得像一个数组,但是,不是数组!
				只有3个点和数组相同:
					1、使用下标
					2、使用length
					3、遍历
 ***arguments可以做的事:
				1、实现重载:通过在函数内部判断arguments,执行不同的操作
				2、以后有没有形参都无所谓
				3、正式开发中,有可能会将多个函数整合为一个函数 - 代码优化

案例:根据arguments的长度来判断
                      function zwjs(){
				if(arguments.length==1){
					return "我的名字叫"+arguments[0];
				}else if(arguments.length==2){
					return "我叫"+arguments[0]+",今年"+arguments[1]+"岁";
				}else if(arguments.length==3){
					return arguments[0]+"今年"+arguments[1]+"岁,喜欢"+arguments[2];
				}
			}
	
			var result=zwjs("代老湿",18,"女");//我的名字叫代老湿,今年18岁,喜欢女
			console.log(result)
			var result=zwjs("袍哥");//我的名字叫袍哥
			console.log(result)
案例:根据arguments的类型来判断
	                 function zwjs(){
				if(typeof(arguments[0])=="string"){
					return "我的名字叫"+arguments[0];
				}else if(typeof(arguments[0])=="number"){
					return "我的今年"+arguments[0]+"岁";
				}else if(typeof(arguments[0])=="boolean"){
					return arguments[0]?"男":"女";
				}
			}

			var result=zwjs(false);
			console.log(result)
案例://创建一个函数:如果传入一个实参则求平方,如果传入两个实参则相加
			function calc(){
				if(arguments.length==1){
					return Math.pow(arguments[0],2)
				}else{
					return arguments[0]+arguments[1];
				}
			}
			
			console.log(calc(8));
			console.log(calc(8,8));
案例:定义calc函数,使用arguments对象计算该方法接收到的所有的参数的算术和
 function calc() {
                if (arguments.length == 1) {
                    return Math.pow(arguments[0], 2)
                } else {
                    return arguments[0] + arguments[1]
                }
            }
            var result = calc(7);
            var result1 = calc(7,2);
            console.log(result)
            console.log(result1)

(十)、匿名函数:没有名字的函数

       1、匿名函数自调:
				为什么:节约内存,因为匿名函数,没有变量引用着,用完,就会立刻释放变量
				语法:
				(function(){
					//以后可以代替全局代码写法,尽量不要再外部再去书写JS(不用担心事件会被释放掉)
				})();

	2、匿名函数回调:将函数作为实参,传递给其他函数调用
        
				1、*学习回调的目的:让你们知道哪些叫做回调:只要不是自调,就是回调
				   arr.sort(function(){})
				   str.replace(reg,function(){})
				   btn.onclick=function(){}

				2、以后ES6技术:箭头函数:简化一切的回调函数

				3、了解一下回调函数的原理

(十一)、函数的执行原理

     1、程序加载时
                 创建执行环境栈ECS:保存着函数调用顺序的数组;
                 先压入全局执行环境栈(全局EC)引用着全局对象window
                 window里面保存着全局变量
                 
     2、定义函数时
                 创建函数对象:封装代码段(保存着当前函数的代码)
                 在当前函数对象中有一个scope(作用域)属性,记录着函数来自的作用域
                 全局函数的scope都是window
     
     3、调用前
                在执行环境栈中压入新的EC(函数的EC)
                创建活动对象(AO):保存着本次函数调用时用到的局部变量
                在函数的EC中有一个scope  chain(作用域链)属性,引用着AO
                AO里面有一个parent属性是scope引用着的对象
     4、调用时
                正是因为前面三步,我们才有了变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错
     5、调用完
                函数的EC会出栈,AO自动释放,局部变量也就自动释放了
***两链一包:
	   (1)作用域链:以函数的EC的scope chain属性为起点,经过AO,逐级引用,形成的一个链式结构
		 作用:查找变量,带来变量的使用规则
           (2)原型链:每个对象都有一个属性:__proto__,可以一层一层的找到每个人的父亲,形参的一条链式结构
                可以找到所有父对象的成员(属性和方法),作用:找共有属性和共有方法的
           (3)闭包     

(十二)、闭包

(1)概念

                   保护一个可以反复使用的局部变量的一种词法结构。
       何时使用:希望保护反复使用的局部变量时
       如何使用:
              两个函数进行嵌套
              外层函数创建出受保护的变量
              外层函数return出一个内层函数
              内层函数要去操作受保护的变量
       
       强调:
           判断是不是闭包,就看有没有两个函数嵌套,返回内层函数,内层函数有没有在操作受保护的变量
           外层函数调用几次,就创建了几个闭包,受保护的变量就有了多少个副本
           同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量
      缺点:受保护的变量永远都不会被释放,因此使用过多会导致内存泄漏,不易多用
       使用场景:
                 防抖节流(3个事件需要防抖节流) 
           
                 1、elem.onmousemove-鼠标移动事件
                 2、input.oninput-每次输入/改变就会触发
                 3window.onresize-每次窗口的大小会改变时就会触发
       
       防抖节流公式:
                 function f1(){
				var timer=null;//3
				return function(){//
					if(timer){clearTimeout(timer);timer=null;}
					timer=setTimeout(function(){
						//操作
					},1000)
				}
			} 
			
			var fdjl=f1();

案例:

1、简单的闭包
                  function factory(){
				var i=0;
				return function(){
					i++;
					return i;
				}
			}
			
			var icbc=factory();
			console.log(icbc());//1
			console.log(icbc());//2
			console.log(icbc());//3
			console.log(icbc());//4
			console.log(icbc());//5
			
			var abc=factory();
			console.log(abc());//1
			console.log(abc());//2
			console.log(abc());//3
			console.log(abc());//4
			
			console.log(i);//reference错误
                        
 1、简单的闭包                  
                        	// 汇率转换器
			function factory(cate){
				return function(money){
					return money*cate;
				}
			}
			
			//人民币转美元
			var rmb2$=factory(0.1579);
			console.log(rmb2$(1000))
			console.log(rmb2$(10000))
			
			//人民币转英镑
			var rmb2yb=factory(0.1162);
			console.log(rmb2yb(1000))
			console.log(rmb2yb(10000))
			
			console.log(rmb2$(1000))
			console.log(rmb2$(10000))
2、防抖节流三事件——onmousemove事件(鼠标移入后里面的数字变化)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>防抖节流-onmousemove事件</title>
    <style>
        div {
            width: 100%;
            line-height: 200px;
            color: #fff;
            text-align: center;
            background-color: aqua;
            font-size: 80px;
        }
    </style>
</head>

<body>
    <div>1</div>
    <script>
        // 防抖节流前
        // var div=document.querySelector("div");
        // div.onmousemove=function(){
        //     div.innerHTML=parseInt(div.innerHTML)+1
        // }

        // 防抖节流后
        var div = document.querySelector("div");
        div.onmousemove=function(){
            animate()
        }

            function fdjl() {
                var timer = null;
                return function () {
                    if (timer) {
                        clearTimeout(timer);
                        timer=null;
                    }
                    timer=setTimeout(function(){
                        div.innerHTML=parseInt(div.innerHTML)+1
                    },1000)
                }
            }
            var animate=fdjl()
    </script>
</body>

</html>

2、防抖节流三事件——input事件

<body>
    <input type="text">

    <script>
        var ipt = document.querySelector("input");
        // ipt.oninput=function(){
        //     console.log(ipt.value)
        // }


// 防抖节流后
        ipt.oninput = function () {
            animate();
        }
        function fdjl(){
            var timer=null;
            return function(){
                if(timer){
                    clearTimeout(timer);
                    timer=null;
                }
                timer=setTimeout(function(){
                    console.log(ipt.value);
                },1000)
            }
        }
        var animate=fdjl();
    </script>
</body>
2、防抖节流三事件——oonresize事件
<body>
    <div>1</div>
    <script>
        // 防抖节流前
        // var div = document.querySelector("div");
        // window.onresize = function () {
        //     if (innerWidth >= 1200) {
        //         div.style.background = "pink";
        //     } else {
        //         div.style.background = "yellow";
        //     }
        // }

        // 防抖节流后
        var div = document.querySelector("div");
        window.onresize = function () {
            animate();
        }
        function fdjl(){
            var timer=null;
            return function(){
                if(timer){
                    clearTimeout(timer);
                    timer=null;
                }
                timer=setTimeout(function(){
                    if(innerWidth>=1200){
                        div.style.background="pink";
                    }else{
                        div.style.background="skyblue";
                    }
                },1000)
            }
        }
        var animate=fdjl();
    </script>
</body>

[二]、预定义的全局函数

                  前辈们提前创建好的方法,我们程序员可以直接使用,在任何位置都可以使用
      1、编码和解码: - 玩悄悄话,你编码发到群里,其他人来解码
	问题:url中不允许出现多字节字符,如果出现会乱码
		utf-8编码格式下,一个汉字,3字节
	解决:发送前,前端将多字符原文编码为单字节字符(数字、字母)
	      发送后,后端将单字节字符解码为多字节原文

		如何:
		  编码:var code=encodeURIComponent("魔兽世界");
  		  解码:var 原文=decodeURIComponent(code);
		其实这个东西在某次浏览器更新后,就当场淘汰了,浏览器自带此功能

     2isFinite(num):判断num是不是无穷大,true->有效数字  false->无穷大
	哪些会为falseNaN,Infinity,分母为0
  
     3、牛逼的:parseInt/FloatisNaNeval
	eval(str):计算字符串,简单来说就是脱字符串的衣服
    
   案例:

         var btns=document.getElementsByTagName("button");
          for(var i=0;i<btns.length;i++){
            btns[i].onclick=function(){
                if(this.innerHTML=="C"){
                   txt.innerHTML="";
                }else if(this.innerHTML=="="){
                    txt.innerHTML=eval(txt.innerHTML);
                }else{
                    txt.innerHTML+=this.innerHTML;
                }
               
            }
        }

image.png

七、数组

          一个变量(内存)保存了多个数据的一个集合结构;当需要保存多个相关数据时就可用数组保存;

(索引数组)

(一)、创建数组

        1)直接量方式:
		var arr=[];//空数组
		var arr=[数据,数据,...];

        2)、构造函数方式:
		var arr=new Array();//空数组
		var arr=new Array(数据,数据,...);
         如果传入的值是纯数字,则会得到长度是num的空数组,遍历会得到num个undefined
       

(二)、获取数据

                数组名[下标];

		特殊:1、下标越界,获取到的是一个undefined

(三)、添加数据

               数组名[下标]=新数据;

	特殊:1、如果下标处已有元素,则为替换
	     2、如果下标越界,会导致你的数组变成一个稀疏数组,中间就会出现很多的undefined,而且下标也不再连续
     
        结论:数组具有3大不限制:
		                 1、不限制元素的长度
		                 2、不限制元素的类型
		                 3、不限制下标越界 - 但是这个操作并不是好事情,强烈不推荐下标越界

(四)、数组对象 - 唯一属性length

       因为自己数下标易数错,提供了一个属性:语法:数组名.length - 获取到数组的长度

	三个固定套路:
		1、向末尾添加元素:arr[arr.length]=新值
		2、获取倒数第n个元素:arr[arr.length-n];
		3、删除末尾的n个元素:arr.length-=n

(五)、遍历数组

         将数组中的每个元素都取出来 执行 相同 或 相似的操作
	 公式:for(var i=0;i<arr.length;i++){
			arr[i];//当前次获取到的元素
	       }

(六)、如何释放一个引用类型

     首先需要看清楚该引用类型的数据由几个变量引用着,只有所有引用着的变量都释放后,该引用类型才会真正的释放。
    (解决:将代码都封装在一个函数中,函数中的变量会自动的释放)

(关联数组/hash数组)

       创建空数组:var arr=[];//空数组
       添加下标并添加元素:arr["自定义下标]=新值
       访问元素:arr["自定义下标]
       !!!
            hash数组由于下标是自定义的,所以length会失效,永远为0,无法使用for循环去遍历,只能用for  in循环;
            for(var i in 数组名){
                         操作
            }

(一)、hash数组的原理

                hash算法:将字符串,计算出一个尽量不重复的数字(地址值) 
                         字符串内容相同,则计算出来的数字也一定是相同的
		添加元素:将自定义下标交给hash算法,得到一个数字(地址值),直接将你要保存的数据放到这个地址之中
		获取元素:将指定的自定义下标交给hash算法,得到一个和当初保存时一样的数字(地址值),通过地址就找到当前保存的数据
                                
       !!!除了undefinednull,js里面的一切东西都是对象(num、boolean、str因为具有包装类型),并且一切对象的底层都是hash数组。  

(二)、二维数组

   数组中的值再次引用了一个数组
       (1)创建
            var arr=[
		       ["张三丰",128,3500],
		       ["张翠山",30,4500],
		       ["张无忌",18,5500]
	            ];
        (2)访问
                   arr[r][c]
       面试中:
             行下标越界:报错(因为根本就没有,得到的是一个undefined,不能使用[]和.操作)
             列下标越界:得到undefined3)遍厉
       for(var r=0;r<arr.length;r++){//外层循环控制行,内层循环控制列
           for(var c=0;c<arr[r].length;c++){
                 console.log(arr[r][c])
           }
       }
             

数组的API

                           前辈们提前定义好的,可以直接使用的

(一)、数组转为字符串 join

                                   xx.toString()    String(x)
         
                                 var str=arr.join("自定义连接符"); 
    固定套路:21、鄙视题:将数组里面的内容拼接为一句话/单词;
				无缝拼接:var str=arr.join("");

			2、将数组拼接为DOM页面元素
    
    案例:无缝拼接
    	var arr=["h","e","l","l","o"," ","w","o","r","l","d"];
			for(var i=0,str="";i<arr.length;i++){
				str+=arr[i];
			}
			console.log(str);//或者下一种方法
			console.log(arr.join(""));
                        
    渲染页面
    <script type="text/javascript">
			var arr=["北京","南京","西京","东京","重庆","北京","南京","西京","东京","重庆","北京","南京","西京","东京","重庆","北京","南京","西京","东京","重庆","北京","南京","西京","东京","重庆"];
			
			sel.innerHTML="<option>"+arr.join("</option><option>")+"</option>";
			
		</script>
                
![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/783fd24467de40a1b8c5a727180d5977~tplv-k3u1fbpfcp-watermark.image?)
    

(二)、拼接数组:concat 添加元素的新方式

     会把传入的实参全部拼接到arr的末尾
                                                var newArr=arr.concat(新值1,...)
     特殊:1、不会修改原数组,只会返回一个新的数组
          2、concat支持传入数组参数,会悄悄的将传入的数组参数自动打散为单个元素后再拼接

(三)、截取子数组:slice

 根据你传入的开始下标截取到结尾下标
		var subArr=arr.slice(starti,endi+1);
		特殊:1、不修改原数组,只会返回一个新子数组
		      2、含头不含尾
		      3、endi可以省略不写,会从starti位置一直截取到末尾
		      4、其实两个实参都可以省略 - 从头截到尾,深拷贝,和以前的按值传递(浅拷贝)不同,一个修改不会影响到另一个
		      5、支持负数参数,-1代表倒数第一个
    
    案例:
     var arr=[1,2,3,4,5,6,7,8];
        var narr=arr.slice(1,4)//截取2-4
        var narr1=arr.slice(1)//从2截到尾
        var narr2=arr.slice()//从头截到尾
        console.log(narr)
        console.log(narr1)
        console.log(narr2)
    
![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8c78aee5543545be8cb16bf9ebcc4742~tplv-k3u1fbpfcp-watermark.image?)
    
 以上三个API不会修改原数组,只会返回一个新数组
    

(四)、删除、插入、替换:splice同之前的arr.length-=n

                删除:var dels=arr.splice(starti,n);//n代表删除的个数
		          特殊:其实splice也有返回值,返回的是你删除的元素组成的一个新数组
		插入:arr.splice(starti,0,值1,...);
		          注意:1、原starti位置的元素以及后续元素都会向后移动
			        2、千万不要插入一个数组,会导致我们的数组一些是一维,一些是二维,遍历时非常的难受
		替换:var dels=arr.splice(starti,n,值1,...);
		          注意:删除的元素个数不必和插入的元素个数相同
     
     案例:
     var arr=[1,2,3,4,5,6,7,8];
        var del= arr.splice(1,4);//删除2,3,4,5     从开始下标,删除n个
        console.log(arr);//[1,6,7,8];
        console.log(del);//[2,3,4,5];

        var arr=[11,22,33,44];//插入
        arr.splice(2,0,"新","来","的")
        console.log(arr)//[11,22,"新","来","的",33,44]

        var arr=[1,2,3,4,5,6,7,8];//替换
        var th=arr.splice(1,3,22,33,44)//替换2,3,4
        console.log(arr);//[1,22,33,44,5,6,7,8]
    

(五)、翻转数组:arr.reverse();

(六)、排序 sort

(一)鄙视题:冒泡排序
   
       从第一个元素开始,依次比较相邻的两个元素,只要前一个元素小于后一个元素,两个元素则交换位置
          公式:
                    var arr=[1,33,5346,65,5767,434,45,544,33]
                    for(var j=0;j<arr.length;j++){
                           for(var i=0;i<arr.length-(j+1);i++){
                                    if(arr[i]<arr[i+1]){
                                               var max=arr[i];
                                               arr[i]=arr[i+1];
                                               arr[i+1]=max;
                                    }
                          }
                   }
(二)开发中:升降序排序
                        arr.sort()
     
     特殊:
     1、默认会将元素们转换为字符串,然后按位PK字符串的ascii码进行排序
     2、按照数字升序排序
               arr.sort(function(a,b){//回调函数:不需要我们程序员调用的函数:悄悄的带有循环,提供了两个形参:a是后一个数,b是前一个数
                              return  a-b
               })
                            //return a-b:如果a>b,返回是一个正数
				//	      如果a<b,返回是一个负数
				//	      如果a==b,返回是一个0,sort根据你返回的结果,来判断两者要不要交换位置
    3、降序排列:
               arr.sort(function(a,b){//回调函数:不需要我们程序员调用的函数:悄悄的带有循环,提供了两个形参:a是后一个数,b是前一个数
			          return b-a;
		   })
       !!!!
              js中,只有数组可以排序,一切具有排序功能的案例,底层都是一个数组
                                
                                
                           
                                
     案例:
    var arr=[1,33,5346,65,5767,434,45,544,33]
    arr.sortfunction(a,b){
         return a-b//升序
    })
    console.log(arr)
    
    
        arr.sortfunction(a,b){
         return a-b//降序
    })
    console.log(arr)

(七)、栈和队列

添加元素和删除元素的新方式
    
   (一)栈
           
     一端封闭,另一端进出的操作。优先使用最新的数据
             开头进:arr.unshift(值,...)//向前添加。
             开头出:var first=arr.shift()//向前删除。
        以上两个api会导致元素的下标发生改变
        
             结尾近:arr.push(值,...)//完全等效与arr[arr.length]或者arr.concat。向后添加
             结尾出:var last=arr.pop()//向后删除
        不会导致元素下标发生改变
    
  (二)队列
  
       其实就是数组,一端进,另一端出。根据先来后到的顺序
       开头进:arr.unshift(值)
       结尾出:var last=arr.pop()
       
       结尾进:arr.push(值)
       开头出:var first=arr.shift()
         
    

扩展:

      周期性定时器:每过一段时间就会执行一次
      
      开启定时器:timer=setInterval(function(){
                                  操作
                 },间隔毫秒数)
   
       停止:clearInterval(timer)
       鼠标移入:onmouseover(触发冒泡)/onmouseenter(不会触发冒泡)
       鼠标移出:onmouseout(触发冒泡)/onmouseleave(不会触发冒泡)
    

八、字符串

(一)、string的概念

                           多个字符组成的【只读】字符数组

(二)、为何字符串也可以叫数组?

   和数组的共同点:
                 支持下标——可以获取某个字符
                 支持length——可以获取字符的长度
                 支持遍历
                 不修改原数组的API,字符串也可以使用(join、concat、slice),但join不会用,因为本来就是字符串。而用concat还不如直接用+拼接。
    不同点:
          数组修改原数组的API,字符串都不可以用,有自己的API

(三)、只读

                   字符串中的所有的API都不会修改原字符串,只会返回新的字符串

(四)、引用类型的对象:11个

	        String(字符串) Number(数字) Boolean(布尔) - 具有包装类型
                
		Array(数组) Function(函数) Math(数学) Date(日期) RegExp(正则) Error(错误) Object(面向对象)
                
		Global - 全局对象:在浏览器端被window对象给代替了:window对象可以省略不写出来

	包装类型:将原始类型的值变为一个引用类型的对象
		  为什么:前辈们发现字符串/数字/布尔经常都会被拿来使用,所以提前提供了包装类型封装为一个引用类型的对象,提供我们一些属性和方法(便于程序员操作)
		  何时使用:只要在你试图用原始类型的值去调用属性或者方法时,会自动套上包装类型
		  何时释放:属性或方法调用完毕后,包装类型自动释放
		  
		为什么undefinednull不能使用.:不具有包装类型,没有任何的属性和方法 

字符串的API

(一)、转义字符 \

    作用:(1)将字符串中和程序冲突的字符转为原文
                "\"   '\"   
          案例:
                        document.write("<h1 style=\"background:green;\">验证通过</h1>")
 	                var str1="我的名字叫\"天棒\"。";
//			var str1='我的名字叫\'天棒\'。';2)、特殊功能:
  			 换行:\n
			 制表符:\t  相当于tab键,大空格
          (3)、*可以书写unicode号 表示一个字
			\uXXXX
			汉字的第一个字:4e00
			汉字的最后一个字:9fa5
                        
                   案例:var str="\u4e00\u9fa5"

(二)、*转换大小写 toUpperCase() toLowerCase()

【统一的】转为大写或小写,再比较,忽略大小写:- 验证码
		大写:var newStr=str.toUpperCase();
		小写:var newStr=str.toLowerCase();

(三)、获取字符串中指定位置的字符的ascii码

	var ascii=str.charCodeAt(i);

	   通过ascii码转回原文
		var 原文=String.fromCharCode(ascii);

(四)、*检索字符串:检查索引/下标: indexOf("关键字",starti)

         从starti位置开始找右侧的第一个关键字的下标
		作用:判断有没有
		var i=str/arr.indexOf("关键字",starti);
		特殊:1、starti可以省略,如果省略则从0开始
		      2、返回值:找到了,返回第一个字符的下标
				 ***没找到,返回-1,其实我们根本不关心下标是几,只关心下标是不是-1,-1代表没找到,不是-1代表找到了
		      3、数组也可以使用此方法
		      4、鄙视题:找到所有关键字的位置
  				var str="no zuo no die no can no bibi";
				var i=-1;
				while((i=str.indexOf("no",i+1))!=-1){
					console.log("找到了:"+i);
				}
                                
     案例:
           var arr=["倚","天","屠","龙","记"];
			var i=arr.indexOf("屠",3);
			console.log(i);

(五)、拼接字符串 concat

                    var newStr=str.concat(str1,str2...) 还不如 +运算

(六)、*截取字符串: slice /substring / substr

                1、**var subStr=str/arr.slice(starti,endi+1);
		2var subStr=str.substring(starti,endi+1);//不支持负数参数
		3、*var subStr=str.substr(starti,n);//截取的个数,不必考虑含头不含尾

(七)、*替换字符串 replace("关键字"/正则表达式,"新内容")

                  var newStr=str.replace("关键字"/正则表达式,"新内容");
        案例:
        		var str="卧槽,我操,我艹,我草,我肏,窝草";
			str=str.replace(/[我卧窝][槽操艹草肏]/g,"**");
			console.log(str);//全部替换为了**

(八)、*****切割/分割字符串 split("自定义切割符")

作用:str <=> arr
		var arr=str.split("自定义切割符");
		特殊:
		1、切割符可以自定义,切割过后返回一个数组,数组中不再包含切割符
		2、如果传入的切割符是一个"",每一个字符都会被切开
     案例:
                 var names="tom&jack&rose&jordan&andy";
		 var arr=names.split("");

扩展

扩展:创建元素并且渲染页面
	1、创建空标签
		var elem=document.createElement("标签名");

	2、设置必要的属性或事件
		elem.属性名="属性值";
		elem.on事件名=function(){函数体} - 事件都可以在创建时提前绑定上

	3、创建好的元素渲染到DOM树上
		父元素.appendChild(elem);

九、正则表达式 正则对象

(一)概念和使用场景:

                定义字符串中【字符出现规则】的表达式,在切割、替换、验证时使用

(二)、定义及知识点

             语法:/正则表达式/
       !!正则默认只要满足后,就不再管理后续操作,后续用户可以乱输入,为了防止乱输入:/^[备选字符集]&/——要求用户输入的东西必须从头到尾完全匹配   
     1、最简单的正则:
                    关键字原文本身:i:忽略大小写      g:全部
                    
                    案例:替换(默认一次只能替换一个)
                    var str="no zuo no die No can no bibi";
			// str=str.replace("no","yes");
			str=str.replace(/no/ig,"yes");
			console.log(str);
                        
     2、备选字符集:
                语法: /[备选字符集]/
                特殊:1、如果备选字符集中,unicode号是连续,那么中间部分可用-代替
                             一位数字:[0-9]
                             一位数字、字母、下划线:[0-9A-Za-z_]
                             一位汉字:[\u4e00-\u9fa5]
                      2、除了xxx之外,其他都可以: - 很少使用,范围太广了
                      如:[^0-9]  除了汉字之外其余的都可以
                      
     3、预定义字符集:提前定义好的字符集的简化写法
                       一位数字:\d	==>	[0-9]
		       一位数字、字母、下划线:\w  ==> [0-9A-Za-z_]
		       一位空白字符:\s  ==>   空格、制表符、换行
                      一位除了换行外的任意字符:  .    	-	范围太广了
          
          !!!!!不管是备选字符集还是预定义字符集,一个中括号只管一位
          
     4、量词: 规定了字符集出现的次数
          (1)、有明确数量
          
                          字符集{n,m}:前边相邻字符集出现的次数,最少出现n此,最多m次
                          字符集{n,}:前边相邻字符集出现的次数,最少出现n次,多了不限
                          字符集{n}:前边相邻字符集出现的次数,必须出现n次
                                
          (2)、没有明确数量
                           ?:前边相邻字符集出现的次数,可有可无,最多出现一次
                           *:前边相邻字符集出现的次数,可有可无,多了不限
                           +:前边相邻字符集出现的次数,最少出现一次,多了不限
    5、指定匹配位置:
                          ^:以xxx开头
		          $:以xxx结尾
		          特殊:如果^和$同时出现:前加^后加$,代表要求从头到尾完全匹配 - 只要是做验证必须加
                          
    6、选择和分组:
		选择:规则1|规则2 - 可以再多个规则中选择满足的规则进行执行
		分组:添加子规则:(规则1|规则2)
          案例:
                var reg=/^(\d|\s)$/;
    7、密码强度:
                 预判公式:(?![备选字符集]+$)
                 
               案例:4位密码,数字和字母的组合,至少出现一位数字和一位大写字母
		/^[0-9A-Za-z]{4}$/
		          (?![0-9]+$) -> 不能全由数字组成
			  (?![a-z]+$) -> 不能全由小写字母组成
			  (?![0-9a-z]+$) -> 不能全由数字、不能全由小写字母、不能只有他俩的组合组成
				

		/(?![0-9a-z]+$)(?![A-Za-z]+$)[0-9A-Za-z]{4}/;//4位密码,数字和字母的组合,至少出现一位数字和一位大写字母
		/(?![0-9a-z]+$)(?![A-Za-z]+$)(?![A-Z0-9]+$)[0-9A-Za-z]{4}/;//4位密码,数字和字母的组合,三者必须都有

(三)支持正则表达式的字符串API

1、切割:var arr=str.split(reg)

案例:
	var str="tom&jerry%andy~邓如宇";
			var arr=str.split(/[&%~]/);
			console.log(arr);//["tom","jerry","andy","邓如宇"]
                        
2、替换:出现在鄙视题中
               var newstr=str。replaec(正则表达式,"新内容") 
        (1)、基本替换法:
		var newStr=str.replace(reg,"新内容");
		特殊:1、默认只会替换第一个关键字,想要替换所有记得加上后缀g
		      2、替换的内容只能是一个固定的新内容


	(2)、高级替换法
			var newStr=str.replace(/[我卧窝][槽操艹草肏去]+/g,function(a,b,c){
				console.log(a);//正则匹配到的关键字
				console.log(b);//正则匹配到的关键字的下标
				console.log(c);//原文本身
				return a.length==2?"**":"***";
			});

	(3)、格式化:如果替换API使用正则时,并且里面带有分组,那么你会得到更多的形参
			var newStr=str.replace(reg,function(a,b,c...){
				console.log(a);//正则匹配到的关键字
				console.log(b);//第一个分组获取到的内容
				console.log(c);//第二个分组获取到的内容
				return "格式化的东西"
			});

	出现:笔试题中   
        
        
        总结:何时前加^后加$,何时又该加后缀gi?
			1、前加^后加$ - 验证
			2、替换,你希望替换到所有 - 必须加g
                        
         案例:
基本替换
         var str="那天,我去了她家,我说\n"
				   +"我草,你家真大\n"
				   +"她说,卧槽,你怎么开口就是国粹啊\n"
				   +"我说,我想和你去草地坐一坐\n"
				   +"她草草的收拾了一下";
                                   
		// 基本替换法,缺陷:替换的新东西是固定的
			str=str.replace(/[我你她卧][去草槽]+/g,"**");
                        console.log(str) //那天,**了她家,我说 **,你家真大  她说,**,你怎么开口就是国粹啊  我说,我想和**地坐一坐   **的收拾了一下
            
            	//高级替换法
			str=str.replace(/[我你她卧][去草槽]+/g,function(a){
				return a.length==2?"**":"***";
			});
		
			console.log(str);//那天,**了她家,我说**,你家真大
她说,**,你怎么开口就是国粹啊    我说,我想和***地坐一坐  ***的收拾了一下
                   
              //格式化替换:
			var id="500103198602215933";
			var reg=/\d{6}(\d{4})(\d{2})(\d{2})\d{4}/;
			
			id=id.replace(reg,function(a,b,c,d){
				return b+"年"+c+"月"+d+"日";
			})
			
			console.log(id);//1986年02月21日
                        
            //首字母大写
			var str="Lorem ipsum dolor a sit amet, consectetur adipisicing elit. Ducimus obcaecati nemo expedita praesentium rem sequi at aspernatur sed minus adipisci suscipit animi tenetur similique quia amet modi vitae voluptatum? Rerum!"
			var reg=/(\w)(\w*)/g;
			str=str.replace(reg,function(a,b,c){
				return b.toUpperCase()+c;
			})
			console.log(str);
                        
           //去掉开头结尾的空白部分
			var str="				hello world						";
			str=str.replace(/^\s*|\s*$/g,"");
			console.log(str);//hello world

(四)正则对象:

      创建:
	       直接量方式:var reg=/正则表达式/后缀;
	       构造函数方式:var reg=new RegExp("正则表达式","后缀");

       APIvar bool=reg.test(用户输入的内容);
	       
               true->验证成功 false->验证失败
****QQ注册案例:
	1、绑定事件要一起绑定
	2、获取焦点事件 - 提示用户要输入什么
	3、失去焦点事件 - 获取用户输入的,再和正则进行验证,不管对错,都要提示用户
	4、表单绑定提交事件 - 阻止提交return false;
  

十、Math 数学对象

    专门提供了数学计算的API,不需要去创建,直接就可以使用
    
    唯一的属性:Math.PI,浏览器自带

(一)API

取整

 1、上取整:只要超过一点点,就会取下一个整数,此方法小数位数不能超过15位否则会失效:Math.ceil(num);
 
 2、下取整:不管超过多少,都会省略掉小数部分,此方法小数位数不能超过15位否则会失效:Math.floor(num);
 
 3、 四舍五入取整:Math.round(num);
	   问题:四舍五入虽然不错,但是以上三个API只能取整
           
	   解决:棒的:parseFloat(num.toFixed(d));//既有四舍五入功能,又具有保留自定义小数位数的操作,结果是一个字符串

	   鄙视题:*封装一个函数,实现可以自定义保留小数位数并且四舍五入的功能,但是不允许使用toFixed?
			function round(num,d){
				num*=Math.pow(10,d);
				num=Math.round(num)
				num/=Math.pow(10,d);
				return num.toString();
			}

乘方和开方

          *乘方:Math.pow(底数,幂); - 简化连续的乘法
	   开方:Math.sqrt(num); - 只能开平方

最大值和最小值

     语法:Math.max/min(a,b,c,d,e,f....);
	   获取到最大的一个数或者最小的一个数
	   问题:不支持数组参数
	  *解决:Math.max/min.apply(Math,arr);
		 apply:自己没有的方法可以去借用	 
			可以将数组打散为单个参数悄悄进行传入  
案例:

   var arr = [2, 4, 5, 6, 8, 10, 33, 454, 958];
              var  max = arr[0];
            for (var i = 1; i < arr.length; i++) {
                if (max < arr[i]) {
                    max = arr[i];
                }
            }
            console.log(max)

            console.log(Math.max.apply(Math,arr));

绝对值:将负数转为整数

             Math.abs(num);

***随机数:只要页面上具有随机的功能,底层一定用到了随机数


		Math.random() 已经是一个随机数了,随机的小数0-1,有可能取到0,但是绝对不可能取到1 - 意味着能取到最小值,但是取不到最大值
		公式:parseInt(Math.random()*(max-min+1)+min); 

十一、Date对象

                封装了一个日期对象,提供了对日期事件进行操作的API
                何时使用:以后只要网页上跟事件日期相关的,我们都要使用date对象

(一)、创建日期对象

1、*创建当前时间:
			var now=new Date();

2、*创建自定义时间:
			var birth=new Date("yyyy/MM/dd hh:mm:ss");

3、创建自定义时间:
			var birth=new Date(yyyy,MM,dd,hh,mm,ss);
			缺点:月份需要修正:计算机中月份是从0开始到114、复制一个日期对象:
			为什么:日期对象的API都是直接修改原日期对象,使用API后,无法同时保存住旧的日期对象
			何时使用:在调用日期对象的API之前都要先复制,在使用API
			语法:var end=new Date(now);

5var xxx=new Date(毫秒数);
			计算器其实保存的就是从197011日至今的毫秒数

(二)、操作

 1、两个日期对象之间可以相减,得到毫秒差,换算出你想要的任何一部分,看出细节:其实日期对象保存的就是一个毫秒数
 
 2API:
		分量:时间单位
			FullYear Month Date Day
			Hours Minutes Seconds
		1、每一个分量都有一对儿方法:getXXX()/setXXX()
			特殊:1、取值范围:
				 年:当前年份
				 月:0-11
				 日:1-31
				 时:0-23
				 分:0-59
				 星期:0-60代表星期天

			      2、星期只有get,没有set
		
		2、固定套路:对着某个日期直接做加减				
			date.setXXX(date.getXXX()+3)


		3、格式化为字符串:
			国际化日期格式化为本地化日期:date.toLocaleString(); - 垃圾:1、具有浏览器的兼容性问题 2、则不可以再使用日期对象的API,也不会再带有进制操作了
									      好处:转为了字符串可用字符串的API
			解决:自定义format格式化

十二、Number对象 Boolean对象

	看出来了一个点:所有的引用类型底层都具有构造函数创建方式,除了Math,直接量简化写法其实是后续追加的操作    

十三、Error对象

(一)、***浏览器自带4种错误类型:

可以快速找到自己的错误

	语法错误:SyntaxError - 多半都是你的语法符号写错了
	引用错误:ReferenceError - 没有创建就直接使用了
	类型错误:TypeError - 不是你的方法,你却去使用了
	范围错误:RangeError - 只有一个API会碰到:num.toFixed(d);//d取值范围的:0~100之间

(二)、错误处理

 语法:
	try{
		只放入你可能出错的代码
	}catch(err){
		发生错误后才会执行
		console.log(err);//err就是我们的错误提示,英文
		console.log("中文错误提示");
	}
           只要发生错误,就会报错,会导致后续代码终止,我们不希望
     
     错误处理:就算发生错误,我们也不希望报错,而是给一个错误的提示,后续代码可以继续执行
      
      try...catch...的性能非常差,几乎里面的代码效率会被降到最低
	*可以用一个技术代替:if...else...
	
        *开发经验:记住一切的客户端输入/用户输入都是坏人 - 你不必担心,只要你做好防护就不会出现错误(!isNaN,比较运算,正则)
 
 
 案例1var num=parseFloat(prompt("请输入一个数字"));
			var d=parseInt(prompt("请输入此数字的保留小数位数"));
			//try...catch...版本的错误处理
			try{
				console.log(num.toFixed(d));
			}catch(err){
				console.log(err);
				console.log("傻逼,保留小数位只能是0-100之间")
			}

			//if...else...版本的错误处理
			if(d>=0&&d<=100){
				console.log(num.toFixed(d));
			}else{
				console.log("傻逼,保留小数位只能是0-100之间")
			}
			
			console.log("后续代码:轮播、选项卡、购物车、正则验证...");
                        
  案例2//程序甲:封装了一个方法,但是可能会报错
			function round(num,d){
				if(!isNaN(num)&&!isNaN(d)){
					num*=Math.pow(10,d);
					num=Math.round(num);
					num/=Math.pow(10,d);
					return num;
				}else{
					throw new Error("傻逼,传入的必须是一个数字");	
				}
			}
			
			//程序员乙:收账系统,用户输入总价,收款金额,收款金额-总价=找零
			function checkout(){
				// try...catch版本
				try{
					var total=round(prompt("请输入总价"),2);
					var money=round(prompt("请输入收款金额"),2);
					var zl=round(money-total,2);
				}catch(err){
					console.log(err);
				}
				console.log("找零为:"+zl);
				console.log("后续代码");

				//if...else版本
				// var total=prompt("请输入总价");
				// var money=prompt("请输入收款金额");
				// if(!isNaN(total)&&!isNaN(money)){
				// 	var zl=round(money-total,2)
				// 	console.log("找零为:"+zl);
				// }else{
				// 	console.log("傻逼,传入的必须是一个数字")
				// }
				// console.log("后续代码");
			}
			checkout()

(三)、抛出自定义错误

               throw new Error("自定义错误信息") - 只要是报错都会卡主后续代码
               
      案例:
               	function round(num,d){
				if(!isNaN(num)&&!isNaN(d)){
					num*=Math.pow(10,d);
					num=Math.round(num);
					num/=Math.pow(10,d);
					return num;
				}else{
					throw new Error("傻逼,传入的必须是一个数字");	
				}
			}

十四、面向对象

面向对象三大特性:封装、继承、多态

开发方式:面向对象 和 面向过程?

	面向过程:经过 - 开始->结束,我们一直的开发方式都是面向过程,先干什么在干什么最后干什么
	面向对象:对象(属性和方法),js有一句话万物皆对象,假设一个人是一个对象的话:
								属性:姓名、性别、身高、体重、爱好、智商、情商...
								方法:吃饭、睡觉、拉粑粑、跑步、走路、打字、学习...
	为什么要面向对象:现实中所有的数据都必须包含在一个事物中才有意义
	何时使用面向对象:以后做任何操作都要封装在一个对象中

(一)、封装/定义/创建

(1)、直接量方式创建

          语法:  
                      var obj={
				"属性名":属性值,
				...
				"方法名":function(){},
				...
		       }
                       
    强调:1、其实属性名和方法名的""可以不加 - 暂时建议你加上
		      2、访问对象的属性和方法
				*obj.属性名	===	obj["属性名"]
				*obj.方法名()	===	obj["方法名"]();
				建议使用.去访问属性和方法,更简单
				***JS中一切都是对象,一切对象的底层都是hash数组
		      3、访问到不存在的属性,返回undefined
		      4、可以随时随地的添加新属性和新方法
		      5、如果我希望遍历出对象所有的东西,使用for in,obj[i]才能拿到,不要用.会出问题
		      6、***如果你希望在对象的方法里使用对象自己的属性:写为this.属性名

			*****难点:this的指向:
				1、单个元素绑定事件:this->单个元素
				2、多个元素绑定事件:this->当前触发的元素
				3、函数中使用thisthis->谁在调用此方法,this指的就是谁
				4、定时器中this->window

(2)、预定义构造函数方式

                   var obj=new Object();//空对象
			//需要自己后续慢慢追加属性和方法
			obj.属性名=属性值
			obj.方法名=function(){}

以上两个方法都有一个缺陷:一次只能创建一个对象,适合创建单个对象的时候(第一种方法),第二种方法了解即可

(3)、自定义构造函数方法

1、创建自定义构造函数
			function 类名(name,age,salary){
				this.name=name;
				this.age=age;
				this.salary=salary;
			}
			//千万不要在里面创建方法,每个对象都会创建一个方法,浪费内存 - 明天,继承将统一的方法放到原型上

		2、调用构造函数创建出对象
			var obj=new 类名(实参1,...)
                        
                        
 案例:
                      function h52107(name,age,hobby){//h52107类,模板,this出现在构造函数中,this代表的是当前正在创建的对象
				this.name=name;//dry.name="邓如宇";
				this.age=age;//dry.age=18
				this.hobby=hobby;//dry.hobby="抠脚"
				this.zwjs=function(){return "我的名字叫"+this.name+",今年"+this.age+"岁,喜欢"+this.hobby}
			}
			
			var dry=new h52107("邓如宇",18,"抠脚");
			var cf=new h52107("曹芬",18,"穿越火线");
			var cmr=new h52107("曹敏茹",18,"穿越人海");
			console.log(dry);
			console.log(cf);
			console.log(cmr);
 详见案例:封装对象版轮播、选项卡、开关门                       

(二)、继承

(1)、定义

         父对象的成员(属性和方法),子对象可以直接使用
	 为什么:代码重用!节约内存空间!提升网站性能!
         何时继承:只要多个子对象公用的属性和【方法】,都要集中定义在父对象中

(2)、如何找原型对象(父对象)

保存一类子对象共有属性和共有方法的父对象

原型对象分:隐式原型属性和显示原型属性

隐式原型属性
        1、对象名.__proto__;//必须先有一个对象
显示原型属性
	2、构造函数名.prototype;//构造函数名,几乎人人都有,除了Math

(3)、有了原型对象,设置共有的属性和方法:

        1、原型对象.属性名=属性值;//添加了一个共有属性
	2、原型对象.方法名=function(){};//添加了一个共有方法

(三)、继承的鄙视题

1、判断是自有还是共有:

                        1、判断自有:obj.hasOwnProperty("属性名");
				如果结果为true,说明是自有属性,如果结果为false,有两种可能,共有或没有
			2、判断共有:
                        
                        if(obj.hasOwnProperty("属性名")==false && "属性名" in obj){}//obj.hasOwnProperty("属性名")==false && 可以省略
                        
                        
                        
				if(obj.hasOwnProperty("属性名")==false && "属性名" in obj){//in 会自动在obj的原型上进行查找
					共有
				}else{
					没有
				}

			公式:
			if(obj.hasOwnProperty("属性名")){//false
				console.log("自有")
			}else{  
				if("属性名" in obj){
					console.log("共有")
				}else{
					console.log("没有")
				}
			}

2、删除自有和共有

                        自有:修改:obj.属性名=新值;
			      删除:delete obj.属性名;

			共有:修改:obj.共有属性名=新值; - 非常危险的,并没有修改到爸爸,而是在本地添加了一个同名属性
			      删除:delete obj.属性名; - 无效,本地本来就没有此自有属性
			      找到原型,修改/删除原型		

3、如何为老IE的数组添加indexOf

原理:
			if(Array.prototype.indexOf===undefined){//老IE
				Array.prototype.indexOf=function(key,starti){
					starti==undefined&&(starti=0);
					for(var i=starti;i<this.length;i++){
						if(this[i]==key){
							return i;
						}
					}
					return -1;
				}
			}

4、如何判断x是不是一个数组:4种方法:千万别用typeof,只能检查原始类型,不能检查引用类型

1、判断x是否继承自Array.prototype
				Array.prototype.isPrototypeOf(x);

			2、判断x是不是由Array这个构造函数创建
				x instanceof Array;

			3Array.isArray(x); - ES5提供的,只是ES5+,老IE不支持,此方法只有数组

			4、输出【对象的字符串】形式(用到到了多态)
			   在Object的原型上保存着最原始的toString方法
			   原始的toString输出形式:[object 构造函数名]
	
			   固定套路:借用:Object.prototype.toString.apply(x);
				if(Object.prototype.toString.apply(arr)=="[object Array]"){
					console.log("是数组")
				}else{
					console.log("不是数组")
				}

5、实现自定义继承:

                        1、两个对象之间设置继承:
				子对象.__proto__=父对象
			
			2、多个对象之间设置继承:
				构造函数名.prototype=父对象
				时机:应该在开始创建对象之前设置好继承关系
案例:
//单个对象设置继承
			var wsc={
				phone:"iphone100",
				dog:"阿拉斯加",
				gf:"很多",
			}
			var wjl={
				"money":1000000000000000
			}
			var dy={
				"js":150,
			}
			wsc.__proto__=wjl;
			wjl.__proto__=dy;
			
			console.log(wsc.js);

			//多个对象设置继承
			function h52107(name,age,hobby){
				this.name=name;
				this.age=age;
				this.hobby=hobby;
			}
			
			h52107.prototype=dy;
			
			var dry=new h52107("邓如宇",18,"抠脚");
			var cf=new h52107("曹芬",18,"穿越火线");
			var cmr=new h52107("曹敏茹",18,"穿越人海");
			var lmb=new h52107("林明宝",19,"臭宝");
			
			console.log(dry);
			console.log(cf);
			console.log(cmr);
			console.log(lmb);

(四)、多态

                      ***多态:子对象觉得父对象的成员不好,在本地定义了同名成员,覆盖了父对象的成员