JavasScript

110 阅读12分钟

1、*****正则表达式:

 什么是:定义字符串中【字符出现规则】的一个表达式
 何时使用:切割 替换【验证】
 如何使用:语法:/正则表达式/

1、最简单的正则:关键字原文 "no" -> /no/gi 只要用上正则就可以添加后缀

	g:全部	i:忽略大小写

2、备选字符集:/[备选字符集]/

	强调:1、一个中括号,只管一位数字
	           2、正则表达式【默认只要满足条件,不管其他了】,解决:前加^,后加$:/^[备选字符集]$/ - 
		代表要求:用户从头到尾必须完整匹配我们的要求 - 只要是做验证就必然前加^,后加$
	特殊:如果备选字符集中的ascii码是连续的,中间的部分可用-代替掉
		一位数字:[0-9]
		一位字母:[A-Za-z]
		一位数字、字母、下划线:[0-9A-Za-z_]
		一位汉字:[\u4e00-\u9fa5]

		除了数字之外的:[^0-9] - 很少使用,范围太广了

3、预定义字符集:前辈们提前定义好的,我们直接使用的

	目的:简化备选字符集
	一位数字:\d		===	[0-9]
	一位数字、字母、下划线:\w	===	[0-9A-Za-z_]
	一位空白字符:\s		===	包含:空格、换行、制表符	
	
	一位除了换行外的任意字符:.	- 	很少使用,范围太广了

	建议:优先使用预定义字符集,满足不了的时候再用备选字符集自定义

	问题:不管是备选字符集,还是预定义字符集,一个都只管一位

4、量词:规定一个字符集出现的次数

     有明确数量
	字符集{n,m}:前边相邻的字符集,最少出现n次,最多出现m次
	字符集{n,}:前边相邻的字符集,最少出现n次,多了不限
	字符集{n}:前边相邻的字符集,必须出现n次

     无明确数量:
	?: 前边相邻的字符集,可有可无,最多1次
	*: 前边相邻的字符集,可有可无,多了不限
	+:前边相邻的字符集,至少一次,多了不限

5、选择和分组

	选择:多个规则中选择其中一个
		规则1|规则2
	分组:将多个字符集临时组成一组子规则
		(规则1|规则2)

6、指定匹配位置:

	^:开头
	$:结尾
	特殊:如果两者同时出现,要求从头到尾完全匹配 - 只要是做验证,必须加上

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位,数字和字母,必须出现一位大写和一位数字和小写
		

2、*****字符串中支持正则表达式的API

1、切割:

	var arr=str.split("固定切割符"/regexp)

2、*****替换

	1、基础替换法:
		var newStr=str.replace("固定关键字"/regexp,"新内容");

	2、高级替换法:
		var newStr=str.replace("固定关键字"/regexp,function(a,b,c){
			console.log(a);//第一个形参保存的是正则匹配到的每一个关键字
			console.log(b);//第二个形参保存的是正则匹配到的每一个关键字的第一个字符的下标
			console.log(c);//原文本身
			return a.length==2?"**":"***";
		});

3、格式化:在使用replace替换时,如果搭配上了正则,并且正则中加入分组,那么我们的高级替换法会得到更多的形参

		  有几个分组,就会多出几个形参。
		var newStr=id.replace(reg,function(a,b,c,d,e,f,g,h){

//// console.log(a);//第一个形参保存的是正则匹配到的关键字 //// console.log(b);//第二个形参会第一个分组匹配到的关键字 //// console.log(c);//第三个形参会第2个分组匹配到的关键字 //// console.log(d);//第四个形参会第3个分组匹配到的关键字 //// //....说不清楚有多少个,具体看有多少个分组 //// console.log(e);//关键字下标 //// console.log(f);//原文本身 // return c+"年"+d+"月"+e+"日"; // })

3、*****正则对象:API

1、创建正则对象:

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

	为什么有时候要加 前加^后加$,为什么有时候又要加后缀g
		1、验证:前加^后加$ - 用户要和我们的规则完全匹配
		2、替换:加后缀g
	

2、API:var bool=reg.test(用户输入的东西);

	返回true,说明用户验证成功,否则验证失败
            

1、Math对象:提供了一些数学计算的API

强调:不需要创建,直接使用:全局对象windowMath

属性:Math.PI 得到3.1415926

API

1、取整:3种

	1、上取整:超过一点点,就取下一个整数
		var result=Math.ceil(num);

	2、下取整:哪怕超过的再多,也会省略掉小数部分
		var result=Math.floor(num);

	3、四舍五入取整
		var result=Math.round(num);

	//以上三个操作都只能取整:
	//取整的方式:以上三个 + *parseInt + *num.toFixed(0)
	//个人更推荐:num.toFixed(d):优点:具有四舍五入,并且小数位数可以自己设置
				 缺点:返回是一个字符串,搭配上一个parseFloat

	//*笔试题:要求不允许使用toFixed,自己封装出toFixed的操作

2、乘方和开方

                *乘方:var result=Math.pow(底数,幂)
	开方:var result=Math.sqrt(num);//只能开平方

3、最大值和最小值:

	var max/min=Math.max/min(a,b,c,d,....);
	问题:本身不支持数组参数的
	解决:固定用法:var max/min=Math.max/min.apply(Math,arr);
		apply是ES5才会学习的东西,apply具有打散数组的功能

4、绝对值:把负数变为正数

	Math.abs(负数)

5、***随机数:Math.random():

0-1之间取随机的小数,但是有可能取到0,不可能取到1
	有可能取到最小数,但是绝对不可能取到最大数
	公式:parseInt(Math.random()*(max-min+1)+min);
	强调:只要以后网页中某一块有一个随机的功能,那么一定需要用到随机数

   	注意:其实Math还提供了三角函数

2、Date对象:提供了操作日期的API

1、创建:4种

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

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

     3、创建一个自定义时间:
	var birth=new Date(yyyy,MM-1,dd,hh,mm,ss);
	缺点:月份需要进行修正,0 代表 1月

     4、复制一份日期:
	为什么:日期的所有的API都是直接修改原日期对象的,无法获得修改之前的日期
	所以,在执行API之前都要先进行复制,然后在操作复制后的日期
	var end=new Date(start);

2、操作:

     1、两个日期对象之间可以相减,得到一个毫秒差,换算出你想要的任何一部分 - 日期的本质底层保存的就是一个毫秒
		其实创建还有第五种方式:var date=new Date(毫秒)
     2、API:
	分量:时间的单位
	年月日星期:FullYear Month Date Day
	时分秒毫秒:Hours Minutes Seconds Milliseconds
	每一个分量都有一对儿getXXX/setXXX的API
	   get用于获取
	   set用于设置
	特殊:
	   1、取值范围:
		年 - 当前年份的数字
		月 - 0~11- 1~31
		星期 - 0~6:外国人觉得星期天是一周的第一天
		小时 - 0~23
		分秒 - 0~59

	  2、唯独星期不允许设置set

3、建议如果你希望对某个分量做加减

	date.setXXX(date.getXXX()+/-n);
	date.setFullYear(date.getFullYear()+3);//对日期+3

4、格式化日期对象->字符串:

	date.toLocaleString();//locale本地 - 具有兼容性问题,在不同的浏览器显示出来的效果是不一样,一般来说我们都要自定义格式化方法

	日期可以用日期自己的API - 日期屌在日期会自动进制
	字符串也只可以用字符串自己的API
	一旦格式化为字符串则不可用日期的API
            
            

1、Error:错误对象

1、***浏览器自带四种错误类型:可以快速找到自己的错误

语法错误:SyntaxError - 符号写错了
引用错误:ReferenceError - 没有创建就去使用了
类型错误:TypeError  - 不是自己的方法,你却去使用了,最典型的,就是你们经常会undefined.xxxnull.xxx
范围错误:RangeError - 只有一个API会碰到:num.toFixed(d);//d的取值范围只能是0~100之间

2、只要发生错误,就会报错,会导致后续代码不执行(如果APP报错,那会直接闪退),我们程序不希望报错

错误处理:就算发生错误,也不会报错,不希望抛出错误,而是希望给一个错误提示即可,后续代码依然可以继续允许
语法:
   try{
	只放入你可能出错的代码
   }catch(err){
	发生错误的时候才会执行
	console.log(err);
   }

try...catch...的性能非常差,几乎里面的代码效率会被降到最低,所以不推荐使用
*可以用一个技术代替:if...else... 提前预判
*开发经验:一切的用户都是坏人,都要防一手(!isNaN(x)、正则:把用户控死)

3、抛出自定义错误:

throw new Error("自定义提示");

2、*****Function:函数对象:提前创建好的,以后可以反复使用的代码段

1、创建:3种

	1、声明方式:function 函数名(形参列表){函数体;return 返回值} - 完整的提前
	2、直接量方式:var 函数名=function(形参列表){函数体;return 返回值}
	3、构造函数方式:var 函数名=new Function("形参",.....,"函数体;return 返回值");
		何时使用:函数体是动态拼接的

2、调用:如果有return,记得接住结果

	var 结果=函数名(实参);

3、考点:

1、创建的三种方式

2、作用域:变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错

3、声明提前:

4、按值传递:

5、重载overload:相同的函数名,根据传入的实参的不同,自动选择对应的函数执行

		为什么:减轻程序员负担!
		问题:JS不支持重载!
			JS不允许多个同名函数同时存在,如果存在,最后的会覆盖之前的所有
		解决:在【函数中】有一个对象:arguments对象,不需要我们创建,自带
		什么是arguments:是一个类数组对象:但是不是数组,和数组有3个相同点
			1、都可以使用下标
			2、都可以使用length
			3、都可以遍历
		作用:***接受住传到函数内部的所有实参,哪怕你不写一个形参
		可以做的事儿:
			1、实现重载:可以通过在函数内部判断arguments的不同,执行不同的分支操作
			2、以后有没有形参无所谓
			3、正式开发中,有可能会将多个函数整合为一个函数 - 代码优化

6、***匿名函数:没有名字的函数

	     1、匿名函数自调:
		//此函数只会执行一次,执行后会自动释放,优点,自动释放 - 代替全局写法,提升网页的性能
		(function(){
			var a=2;
			console.log(a);
			
			btn.onclick=function(){
				console.log("释放了码");
			}
			//引用类型:在还有人使用的情况下,是不会自动释放的
		})();

	     2、匿名函数回调:将函数作为实参,传递给了其他函数调用 - 没有名字就是匿名函数,匿名函数只有自调和回调,没有自调,则是回调
		1、学习回调函数的目的:让你们知道哪些属于回调函数	
			arr.sort(function(a,b){return a-b;})
			str.replace(reg,function(){})
			btn.onclick=function(){}
		这些东西都是前辈们规定好的,我们只能学习如何使用的

		2ES6 - 一切的回调函数,都可以简化为箭头函数

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

7、*****闭包 -

            明天讲,面试题:两链一包(作用域链、原型链、闭包)
            
            

1、*****Function

***作用域:

1、全局:成员,随处可用,可以反复使用,缺点:容易被污染

2、函数:成员,只能在函数调用时内部可用,不会被污染,缺点:一次性的,是会自动释放的

***函数的执行原理:

1、程序加载时:
     创建执行环境栈(ECS): 保存函数调用顺序的数组
     首先压入全局执行环境(全局EC)
     全局EC引用着全局对象window
     window中保存着全局变量

2、定义函数时
      创建函数对象:封装代码段
      在函数对象中有一个scope(作用域)属性:记录着函数来自的作用域是哪里
      全局函数的scope都是window

3、调用前
      在执行环境栈(ECS)压入新的EC(函数的EC)
      创建活动对象(AO):保存着本次函数调用时用到的局部变量
      在函数的EC中有一个scope chain(作用域链)属性引用AO
      AO有一个parent属性是函数的scope引用的对象

4、调用时
      正是因为有前面三步,我们才有了变量的使用规则:优先使用局部的,局部没有找全局,全局没有就报错

5、调用完:
      函数的EC会出栈,没人引用AO,AO自动释放,所以局部变量也就释放了

两链一包:

作用域链:以函数的EC中的scope chain属性为起点,经过AO,逐级引用,形成的一条链式结构,就称之为叫做作用域链

	作用:变量的使用规则,查找变量

 *****闭包:希望保护一个可以【反复使用的局部变量】的一种词法结构,其实函数一个函数,写法和以后有点不一样
何时使用:希望保护一个可以【反复使用的局部变量】
如何使用:41、两个函数相互嵌套
	2、外层函数创建出受保护的变量
	3、外层函数要return返回内层函数
	4、内层函数要操作受保护的变量

强调:

   1、判断是不是闭包,有没有两个函数嵌套,返回内层函数,内层函数在操作受保护的变量
   2、外层函数调用了几次,就创建了几个闭包,受保护的变量就有了几个副本
   3、同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量

缺点:受保护的变量,永远不会被释放,使用过多,造成内存泄漏
使用场景:防抖节流 - 有三个事件需要做防抖节流:执行的速度非常的快,减少DOM树的渲染
	1、elem.onmousemove - 鼠标移动事件
	2、input.oninput            - 输入内容有变化就会触发
	3window.onresize	      - 当前窗口的尺寸如果发生了变化就会触发:JS版本的媒体查询
	公式:
		elem.on事件名=function(){
			inner();
		}
		function fdjl(){
			var timer=null;//1 2 3 - 定时器序号
			return function(){
				if(timer){clearTimeout(timer);timer=null}
				timer=setTimeout(function(){
					//操作
				},1000)
			}
		}		
		var inner=fdjl();

对象:Array/String/Function/Math...对象具有属性和方法,都是预定义好,现在我们学习自定义对象

2、*****面向对象:Object:三大特点:封装、继承、多态

1、***开发方式:

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

2、封装对象:创建自定义对象:3种

1、直接量方式:

		var obj={
			"属性名":"属性值",
			...
			"方法名":function(){},
			...
		}
		强调:
			1、其实属性名和方法名的""可以省略不写的 - 暂时建议写上,以后我们会学习JSON数据格式,要求必须加""
			2、访问对象的属性和方法
				*对象名.属性名	===	对象名["属性名"]
				*对象名.方法名();	===	对象名["方法名"]();
				JS种一切对象的底层都是hash数组
			3、访问到不存在的属性或者方法,返回undefined
			4、可以后期随时随地的添加新属性和新事件
			5、如果希望遍历出对象中的所有东西
				for(var i in 对象名){
					对象名[i]
				}
			6、***如果你希望在对象的【方法中】使用对象自己的属性:写为:this.属性名
				*****难点:this的指向:
					1、*单个元素绑定事件:this->这个元素
					2、*多个元素绑定事件:this->当前触发事件的元素
					3、***函数中使用thisthis->谁在调用此方法,this指向就是谁
					4、定时器this->window
					5、箭头函数this->外部对象

2、预定义构造函数方式:

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

	以上两种方法创建都有一个缺陷:一次只能创建一个对象,适合创建单个对象的时候使用(第一种方式)

3、自定义构造函数方式:- 何时创建多个对象:2步

1、创建自定义构造函数
			function 类名(name,age,salary){
				this.name=name;
				this.age=age;
				this.salary=salary;
			}
2、反复调用自定义构造函数创建出对象
			var xxx=new 类名("实参",...)

	面向对象:
	       优点:1、逼格高,所有的属性和方法都是保存在一个对象之中 -更符合现实生活:代码风格、代码速度
		2、每个功能特地的分开写 - 便于维护
		3、铁锁连舟 - 一个方法触发多个方法联动
	       缺点:1、对新手不友好 - this的指向