二阶段第三周JS知识点整理

66 阅读19分钟

Monday

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

什么是:定义字符串中字符出现【规则】的表达式!
何时使用:切割、替换、【验证】
如何使用:语法:/正则表达式/
	1、最简单的正则:关键字原文"no"	->	/no/后缀
		后缀:g:全部			i:忽略大小写

	2、备选字符集:/^[备选字符集]$/
		强调:
			1、一个中括号,只管一个字符
			2、正则表达式默认只要满足就不管后续了,用户只要满足后面就可以乱来,我们希望从头到尾完全匹配,解决:前加^后加$ - 要求从头				      到尾完全匹配 - 【只要你是做验证就必须加这个东西】
			3、如果备选字符集的ascii码是连续的,可以用-省略掉中间的部分
				比如:一位数字:[0-9]
				          一位字母:[A-Za-z]
				          一位数字、字母、下划线:[0-9A-Za-z_]
				          一位汉字:[\u4e00-\u9fa5]

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

	3、预定义字符集:前辈们提前定义了一些字符集,方便我们程序员使用
		一位数字:\d
		一位数字、字母、下划线:\w
		一位空白字符(空格、制表符、换行):\s

		一个除了换行外的任意字符:.	-  很少使用,范围太广了

	建议:优先使用预定义字符集,预定义满足不了的我们再用备选字符集补充
	问题:不管备选字符集还是预定义字符集,一个都只管一位

	4、量词:规定一个字符集出现的次数:
		1、有明确数量:
			字符集{n,m} - 前边相邻的字符集最少出现n次,最多出现m次
			字符集{n,} - 前边相邻的字符集最少出现n次,多了不限
			字符集{n} - 前边相邻的字符集必须出现n次

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

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

	6、指定匹配位置:
		^:开头
		$:结尾
		特殊:两者同时使用,前加^后加$:表示从头到尾要求完全匹配 -  只要你做【验证】

	7、预判:作用:专门用于做密码强度:
		4位密码,数字和字母的组合,至少出现一位数字和一位大写
		/^(?![预判]+$)[0-9A-Za-z]{4}$/

		预判公式:
			(?![0-9]+$); -> 不能全由数字组成,可能由大写、小写、特殊符号、汉字、日文、韩文....,具体用户能输入什么要看后面放了什么字符集
			(?![0-9A-Z]+$) -> 不能全由数字组成,也不能全由大写组成,也不能全由数字和大写的组合
			/^(?![0-9a-z]+$)(?![A-Za-z]+$)(?![A-Z0-9]+$)[0-9A-Za-z]{4}$/; - 用户可以数字、大写、小写,但是不能是数字和小写的组合,也不										        能是大写和小写的组合,也不能是大写和数字的组合
	

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

1、切割:var arr=str.split("切割符"/RegExp)
	可以用正则让我们的切割符变为可变的

2、*****替换:
	1、基础替换法:
		var newStr=str.replace(/正则/g,"新内容");
		//replace支持正则,并且要搭配上后缀g才可以找到全部

	2、高级替换法:
		var newStr=str.replace(/正则/g,function(a,b,c){
			console.log(a);//*正则匹配到的关键字
			console.log(b);//正则匹配到的关键字的第一个字符的下标
			console.log(c);//原文本身
			//判断关键字的长度
			return a.length==2?"**":a.length==3?"***":"****";
		});

	3、格式化:
		var str="500103198602215933";
		var reg=/(\d{6})(\d{4})(\d{2})(\d{2})(\d{4})/;
		str=str.replace(reg,function(a,b,c,d,e,f,g,h){
			在replace的时候,正则如果出现了分组,我们会得到更多的形参,
			console.log(a);//正则匹配到的关键字
			console.log(b);//第1个分组保存到的内容
			console.log(c);//第2个分组保存到的内容
			console.log(d);//第3个分组保存到的内容
			console.log(e);//第4个分组保存到的内容
			console.log(f);//第5个分组保存到的内容
			....具体多多少不清楚,要看你有多少个分组
			console.log(g);//正则匹配到的关键字的第一个字符的下标
			console.log(h);//原文本身

			return c+"年"+d+"月"+e+"日";
		})
		console.log(str);

3、正则对象:

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

2API:验证/测试:
	var bool=reg.test(用户输入的内容);
	true->验证成功	false->验证失败

总结:何时前加^后加$(验证),何时又需要加后缀g(替换)?

Tuesday

1、Math对象:专门提供了数学计算的API

强调:Math不需要创建,可以直接使用的

属性:Math.PI - 其实我们程序员根本不用自己创建那个3.1415926,在js中自带此操作

API:
	1、取整:3种
		1、上取整:超过一点点,就会取下一个整数
			var num=Math.ceil(num);
			特殊:num小数位数不能超过15位,一旦超过,上取整失效,直接取整数

		2、下取整:无论超过多少,都会省略掉小数部分
			var num=Math.floor(num);

		3、四舍五入取整:
			var num=Math.round(num);//只看小数的第一位

		以上三个API都是垃圾:都是在取整,只能取整
		取整的方式:以上三个、*parseInt、*num.toFixed(d);//d代表的是保留的小数位数,0其实也可以取整,而且此方法也带有四舍五入,
							   唯一的缺点就是结果是一个字符串,建议搭配上一个parseFloat();

		//鄙视题:不允许使用toFixed的情况下,自己封装一个函数,实现toFixed的操作
		function round(num,d){
			num*=Math.pow(10,d);
			num=Math.round(num);
			num/=Math.pow(10,d);
			return num;
		}
		var result=round(Math.PI,4)

	2、*乘方:解决你的连乘操作:Math.pow(底数,幂);
	      开方:只能开平方:Math.sqrt(num);

	3、*最大值和最小值:var max/min=Math.max/min(num1,num2,num3,num4,....);
		问题:本身不支持数组参数
		解决:固定用法:var max/min=Math.max/min.apply(Math,arr);
			apply是ES5提供的新操作,其中一个作用就是可以将数组悄悄的打散

	4、绝对值:将负数转为正数
		Math.abs(负数);

	5、***随机数的原理:Math.random() 就已经得到随机数了,在0~1之间取出一个随即小数,
			搭配上parseInt过后,只能取到0,但是绝对不能取到1,意味着能取到最小值,但是取不到最大值
			所以衍生出了我们的公式
		随机整数公式:parseInt(Math.random()*(max-min+1)+min);

2、Date:日期对象,提供了对时间进行操作的API

1、创建:41*获取当前时间:
		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~110代表14*复制日期:
		为什么:日期的所有的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;
			其中getXXX负责获取某一个分量的值
			其中setXXX负责设置某一个分量的值
		*特殊:
			1、取值范围:
				FullYear - 当前年份的数字
				Month - 0~11
				Date - 1~31
				Day - 0~60代表星期天,在外国人的眼里,星期天是一个星期的第一天
				Hours - 0~23
				Minutes、Seconds:0~59

			2、日期对象在控制台长得很像字符串,但是不是字符串,在设置时如果超过了取值范围,他会自动进制

			3Day是不允许设置的,星期是一天一天推算过来的,并不是设置的

			4、如果希望对某个分量进行加减操作
				date.setXXX(date.getXXX()+/-n)

			5、格式化日期为【本地】【字符串】:
				date.toLocaleString(); - 垃圾:具有兼容性问题,解决:自定义format函数格式化
				用了此方法会失去一些东西:日期对象的自动进制、日期对象的API - 因为转为了一个字符串了,但是你得到了字符串的API

Wednesday

1、Error:错误对象

以后工作/学习中:
	1、快速找错
	2、只要是用户输入,记得要防用户

1、***浏览器自带4种错误类型:快速找错
	语法错误:SyntaxError - 多半都是符号写错了
	引用错误:RerferenceError - 没有创建就去使用了
	类型错误:TypeError - 不是你的方法,你却去使用了,很有可能拿到的是一个undefined再去做操作
	范围错误:RangeError - 只有一个API会碰到:num.toFixed(d);//d取值范围:0~100之间

2、只要发生错误,就会报错:会导致后续代码终止了(如果是桌面应用更恐怖:闪退了),不执行了,我们不希望,用户的体验感差了
      错误处理:就算发生错误,我们也不希望报错,而是给出一个错误提示,让后续代码可以继续执行
      语法:
	try{
		只放入你可能出错的代码
	}catch(err){
		只有发生错误才会执行
		err - 保存着我们的错误信息,但是是英文
	}

      try...catch...的性能非常差,几乎所有代码里效率最低的
      *可以用一个技术代替:分支结构if...else...
      *开发经验:记住一切的客户端输入/用户输入都是坏人 - 你不必担心,只要你做好防护就绝对不会出错(!isNaN、正则)

3、抛出自定义错误:
	throw new Error("自定义错误信息");

2、*****Function:函数:预定义好的,以后可以反复调用的代码段

考点:
	1、***创建:31、*声明方式:function 函数名(形参列表){函数体;return 结果;} - 完整的声明提前
		2、直接量方式:var 函数名=function(形参列表){函数体;return 结果;}
		3、构造函数方式:var 函数名=new Function("形参1",...,"函数体;return 结果;");
			何时:如果你的函数体不是固定的,而是动态拼接的一个字符串
			var arr=[3,21,453,65,78,6,87,9,8];
			var user=prompt("数组排序,如果输入a-b则为升序排列,如果输入b-a则为降序排列")
			var compare=new Function("a","b","return "+user);
			arr.sort(compare);

	2、***调用时:如果由return,记得要接住
		var result=函数名(实参列表);
	3、***作用域
	4、***声明提前
	5、***按值传递
	6、***重载(overload):相同的函数名,传入不同的实参,可以自动选择对应的函数执行操作
		为什么:减轻程序员的负担!
		问题:js的语法不支持重载!
		          js不允许多个同名函数同时存在,如果同时存在,最后的会覆盖之前所有的
		解决:在【函数中】有一个对象 - arguments对象
			什么是arguments:只能在函数中使用,自动创建,是一个类数组对象(类似数组)
				作用:***可以接受住所有的实参
			只有3个点和数组相同:
				1、可以使用下标
				2、可以使用length
				3、可以遍历

		***arguments可以做的事:
			1、变相实现重载:通过在函数内部判断arguments,执行不同的操作
			2、以后有没有形参已经无所谓了
			3、正式开发中,有可能将多个函数整合为一个函数 - 提升代码性能 - 代码优化

	7、***匿名函数:没有名字的函数
		1、匿名函数自调:只能执行一次
			为什么:节约内存,因为匿名函数,没有变量引用着,用完就会立刻被垃圾回收器回收掉
			如何:
				(function(){
					//以后可以代替全局代码写法,尽量不要写到外部,而且不必担心事件会被释放
				})();

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

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

			3、了解一下回调函数的原理:回调函数调用过吗?
				只不过不是我们程序员在调用,而是创建此方法的前辈们,提前在他的函数内部,帮我们调用好了

Thursday

1、*****Function

作用域:
	全局:随处可用,可以反复使用,缺点:容易被污染

	函数:只能在函数调用时内部使用,不会被污染的,缺点:一次性的,用完会自动释放

***函数的执行原理:
	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自动释放,局部变量也就自动释放了
  ***面试题:两链一包
  	作用域链:以函数的EC的scope属性为起点,经过AO,逐级引用,形成的一条链式结构,我们就称之为叫做作用域链
  		作用:查找变量,带来了变量的使用规则:优先使用局部的,局部没有找全局,全局没有就报错
  	闭包:希望保护一个可以【反复使用的局部变量】的一种词法结构
  		作用:防抖节流
  		缺点:内层泄漏
1、*****闭包:希望保护一个可以【反复使用的局部变量】的一种词法结构,其实函数一个函数,写法和以前有点不同
	何时使用:希望保护一个可以【反复使用的局部变量】的时候
	如何使用:
		1、两个函数进行嵌套
		2、外层函数创建受保护的变量
		3、外层函数返回return出内层函数
		4、内层函数要去操作受保护的变量

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

	缺点:受保护的变量,永远永远都不会被释放,使用过多,内层泄露 - 不可多用
	使用场景:防抖节流
		1、三个事件需要防抖节流
			1、elem.onmousemove - 鼠标移动事件
			2、input.oninput - 每次输入/删除就会触发
			3window.onresize - 每次你修改窗口尺寸都会触发
	
		防抖节流公式:
		function fdjl(){//停开停开停开停开停开停开停开停开停开停开停开
			var timer=null;
			return function(){
				if(timer!=null){clearTimeout(timer)}
				timer=setTimeout(function(){
					//操作
				},1000);
			}
		}
		var result=fdjl();
		此内层函数再在你需要事件中调用即可

2、*****Object:对象:

Array、Function、Date、Math... 对象具有属性和方法,都是预定义好的,我们现在可以学习自定义对象 面向对象:三大特点:封装、继承、多态

    1、***开发方式:21、面向过程:经过 - 开始->结束,我们一直使用的开发方式都是面向过程:先干什么再干什么最后干什么
	2、面向对象:对象(属性和方法),JS有一句话万物皆对象,假设一个人是一个对象的话:
		属性:姓名、性别、年龄、身高、体重、爱好...
		方法:吃饭、睡觉、拉粑粑、跑步、走路、学习...
	为什么要面向对象:现实生活中所有的数据都必须包含在一个事物之中才有意义。更符合现实。
	何时使用面向对象:以后做任何操作都要封装在一个对象里 - 建议:对初学者不太友好。

2、封装:创建自定义对象:3种方法
	1、*直接量方式:
		var obj={
			"属性名":属性值,
			...
			"方法名":function(){},
			...
		}

		强调:
			1、其实属性名和方法名的""可以省略 - 暂时建议你加上
			2、访问对象的属性和方法
				*obj.属性名;	===	obj["属性名"]
				*obj.方法名();	===	obj["方法名"]();
				***js中一切都是对象,一切对象的底层都是hash数组
			3、访问到不存在的属性,返回undefined
			4、可以随时随地添加新属性和新方法
				obj.属性名=属性值;
				obj.方法名=function(){};
			5、如果我们希望遍历出对象所有的东西,应该使用
				for(var i in lzh){
					console.log(lzh[i]);
				}
			6、*****如果你希望在对象的方法里使用到对象自己的属性:this.属性名;

			     *****难点:this的指向:
				1、单个元素绑定事件:this->单个元素
				2、多个元素绑定事件:this->当前触发事件的元素
				3、函数中使用this->谁在调用此方法,this指的就是谁
				4、定时器中的this->window
				5、箭头函数this->外部对象
				6、构造函数中的this->当前正在创建的对象

	2、预定义构造函数:var obj=new Object();//空对象
			//需要自己后续慢慢追加属性和方法
			obj.属性名=属性值;
			obj.方法名=function(){};

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

	3、*自定义构造函数:2步 - 适合创建批量对象
		1、创建自定义构造函数
			function 类名(name,age,hobby,...){
				this.name=name;
				this.age=age;
				this.hobby=hobby;
				...
			}
			//构造函数中的this->当前正在创建的对象
			//千万不要再里面创建方法,每个对象都会创建出一个方法,每一个方法/函数也是一个对象,浪费内存 - 下周一继承可以解决
		
		2、调用构造函数创建出对象
			var obj=new 类名(实参,...)

	面向对象:
		优点:
			1、逼格高,所有的属性和方法都保存在一个对象之中 - 更符合现实
			2、每个功能特地的分开写 - 哪怕不写注释,也便于以后维护
			3、铁锁链舟 - 一个方法触发多个方法联动

		缺点:对新手不友好,this的指向非常的恶心

Friday

*****Object:

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

2、***如何找到原型对象:保存一类子对象共有属性和共有的方法的父对象:
	1、对象名.__proto__;//必须现有一个对象
	2、构造函数名.prototype;//构造函数名:Array、Date、RegExp、Object、自定义构造函数...几乎人人都有,除了Math

3、*面试题:两链一包作用域链(查找变量)、原型链(查找属性和方法)、闭包(保护反复使用的局部变量) 每个对象都有一个属性:.proto,可以一层一层的找到每个的父亲,形成的一条链式结构,就称之为叫做原型链 可以找到所有父对象的成员(属性和方法),作用:查找属性和方法,哪怕自己没有 最顶层的是Object的原型,上面放着一个我们眼熟的toString,怪不得人人都可以用到toString(); JS万物皆对象

4、有了原型对象,设置共有属性和共有方法:
	原型对象.属性名=属性值;
	原型对象.方法名=function(){};

***继承具有非常多的鄙视题:
	1、判断是自有还是共有:
		1、判断自有:obj.hasOwnProperty("属性名");
			true->自有	false->没有或共有
		2、判断共有:
			if(obj.hasOwnProperty("属性名")==false&&"属性名" in obj){
				//in会自动在obj的原型链上进行查找,如果找到了结果为true,如果没找到结果为false
			}

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

	2、修改/删除自有和共有:
		修改和删除自有:
			修改:obj.属性名=新值
			删除:delete obj.属性名;

		修改和删除共有:一定要找到原型对象做操作,千万不要在本地做操作,修改(添加了一个同名属性),删除(无效果)
			修改:原型.属性名=新值
			删除:delete 原型.属性名;

	3、如何为老IE的数组添加indexOf方法,而且这道题不是固定的,可能是为一类人添加方法
		if(Array.prototype.indexOf===undefined){
			//老IE,加这句话是为了性能:主流浏览器有这个方法,完全不需要我去创建,我这一拖代码只希望老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;
			}
		}
		更多的案例见:07鄙视-为老IE的数组添加indexOf方法.html

	4、如何判断x是不是一个数组:千万别用typeof(),只能检查原始类型,不能检查引用类型
		4种:
		1、判断x是不是继承自Array.prototype
			Array.prototype.isPrototypeOf(x);
			//true - 说明是数组		false - 说明不是数组
		
		2、判断x是不是由构造函数Array创建出来的
			x instanceof Array;

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

		4、输出【对象的字符串】形式
		     在Object的原型上保存着最原始的toString方法
		     原始的toString输出形式:[object 构造函数名]
		     ***多态:子对象觉得父对象的成员不好用,在本地定义了同名成员,相当于覆盖了父对象的成员。
		     不严格的说法:同一个函数名,不同的人来使用,出现的效果是不一样的
		     固定套路:借用:Object.prototype.toString.apply(x); === "[object Array]" 说明一定是数组

	5、实现自定义继承:
		1、两个对象之间设置继承
			子对象.__proto__=父对象;

		2、批量设置继承:
			构造函数名.prototype=父对象;
			时机:先继承,再创建。