JS知识体系梳理-4

正则

定义:是一个用来处理字符串的规则javascript

正则只能用来处理字符串css

处理通常包含两方面java

  • 验证当前字符串是否符合某个规则"正则匹配"
  • 把一个字符串中符合规则的字符获取到"正则捕获"

建立正则的两种方式

let reg1=/^\d+$/g;//=>字面量方式
let reg2=new RegExp("^\\d+$","g");//=>构造函数方式
复制代码

元字符和修饰符

正则两个斜杠之间包起来的都是"元字符",斜杠后面出现的都是"修饰符"ajax

经常使用修饰符
`i`:ignoreCase  忽略大小写匹配
`m`:multiline 多行匹配
`g`:global  全局匹配
复制代码
经常使用元字符
量词元字符

让其左边的元字符出现多少次正则表达式

`?`:出现零到一次
`*`:出现零到屡次
`+`:出现一到屡次
`{n}`:出现N次
`{n,}`:出现N到屡次
`{n,m}`:出现N到M次
复制代码
特殊元字符
`\d`:0~9之间的一个数字
`\D`:非0~9之间的任意字符
`\w`:"数字、字母、下划线"中的任意一个
`\s`:匹配任意一个空白字符(包括\t制表符[TAB]键四个空格)
`\b`:匹配边界符  "zhu"(z左边和u右边就是边界)  "zhu-feng"(z左边、u右边、f左边、g右边是边界)
`\n`:匹配一个换行符
`\`:转义字符  把一个普通字符转义为特殊的字符,例如:\d;把有特殊含义的转换为普通意思,例如:`\.` 此处的点就不是任意字符,而是一个小数点
`.`:匹配除了\n之外的任意字符
`^`:以某个元字符开头
`$`:以某个元字符结尾
`|`:或者  例如`x|y`表明x或者y中的任意一个
`[]`:中括号中的任意一个字符  例如`[xyz]`表明x或者y或者z中的任意一个
`[^]`:除了中括号中字符之外的任意字符 例如`[^xyz]`表明除了x/y/z之外的任意字符
`[a-z]`:获取a-z中的任意一个字符([0-9] 等价于\d...)
`[^a-z]`:除了a-z的任意字符
`()`:正则分组
`(?:)`:当前分组只匹配不捕获
`(?=)`:正向预查
`(?!)`:反向预查
复制代码
中括号的一些细节

中括号中出现的元字符通常都是表明自己含义的,可是\d在中括号中仍然表明的是0~9中的一个数字express

let reg=/^.$/;
//=>一个正则设置了^和$,那么表明的含义其实就是只能是xxx
---------------------
let reg=/^[.]+$/;
console.log(reg.test("n"));//=>false
console.log(reg.test("1"));//=>false
console.log(reg.test("nn"));//=>false
console.log(reg.test("..."));//=>true

---------------------
let reg=/^[\d]+$/;
console.log(reg.test("d"));//=>false
console.log(reg.test("0"));//=>true

复制代码

中括号中出现的两位数,不是两位数,而是两个数字中的任意一个编程

let reg=/^[18]$/;
console.log(reg.test("1"));//=>true
console.log(reg.test("8"));//=>true
console.log(reg.test("18"));//=>false
-------------------
let reg=/[18]/;
console.log(reg.test("18"));//=>true
console.log(reg.test("81321"));//=>true
console.log(reg.test("22"));//=>false
-------------------
let reg=/^[12-65]$/;
//=>这个正则的意思是,只能是1,5,或者2-6之间的一个数
console.log(reg.test("1"));//=>true
console.log(reg.test("7"));//=>false
复制代码

案例json

匹配年龄在18~65之间设计模式

/* * 18~19 1[89] * 20~59 [2-5]\d * 60~65 6[0-5] */
 
let reg=/^(1[89])|([2-5]\d)|(6[0-5])$/
复制代码

案例数组

验证身份证号,并把生日,地域,性别信息匹配出来

//=>前6位表明地域,倒数第二位偶数表明女,奇数表明男

let reg=/^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d|x)$/

let str="130828199012040617"

console.log(reg.exec(str));
["130828199012040617","130828","1990","12","04","1",index:0,input:"130828199012040617"]
复制代码
普通元字符

只要在正则中出现的元字符(在基于字面方式建立),除了特殊和有量词意义的之外,其他的都是普通元字符

正则的EXEC方法(正则捕获)

定义

用于检索字符串中的正则表达式的匹配,并返回包含该查找结果的一个数组。若是能够匹配获取的结果是一个数组,若是不能匹配获取的结果是null

let str="zzzzzzzz2018sssssss2019";
let reg=/\d+/;
console.log(reg.exec(str));
复制代码

若是咱们只在匹配的时候,想要获取大正则中部分信息,能够把想要捕获到的内容使用小括号包起来,造成一个分组,这样在捕获的时候,不只能够把大正则匹配的信息获取到,并且还单独的把小分组匹配的部分信息也捕获到了(分组捕获)

若是使用正则分组不是为了捕获信息,只是为了改变优先级或者进行分组引用,能够在分组的前面加上"?:",表明只去匹配,可是不把这个分组内容捕获

正则的捕获有懒惰性

执行一次exec只能捕获到第一个匹配的内容,剩余的默认捕获不到,哪怕是全局下执行一次exec只能捕获一个匹配的内容,在全局下想要得到全部匹配的内容须要不停的执行exec方法,直到返回值是null为止

lastIndex是正则实例上内置的属性,,lastIndex不变致使了正则捕获的懒惰性,不设置全局匹配的状况下,无论怎么操做当前正则实例的lastIndex属性值一直为0

let str="zzzzzzz2018sssssss2019";
let reg=/\d+/;
console.log(reg.exec(str));//=>["2018"]

console.log(reg.lastIndex);//=>0
console.log(reg.exec(str));//=>["2018"]
console.log(reg.lastIndex);//=>0
console.log(reg.exec(str));//=>["2018"]
console.log(reg.lastIndex);//=>0

------
???改变后lastIndex已经变了,为何不按照lastIndex查找的匹配的机制去匹配

//=>即便手动改变lastIndex值也没有做用
console.log(reg.exec(str));//=>['2018']
reg.lastIndex = 11;
console.log(reg.lastIndex);//=>11
console.log(reg.exec(str));//=>['2018']
console.log(reg.exec(str));//=>11
复制代码

解决正则懒惰性的方案

惟一的解决方法: 须要加全局修饰符G,此时只要屡次执行exec方法,就能够匹配到字符串中全部匹配的内容

lastIndex意思是正则表达式开始下一次查找的索引位置,第一次查找完了的时候会把lastIndex的值设为匹配到的字符串的最后一个字符的索引位置加1,第二次查找的时候会从lastIndex这个位置开始,后面以此类推。若是没有找到,则会把lastIndex重置为0。

let str = 'zzzzzzz2018ssssss2019';
let reg = /\d+/g;
console.log(reg.lastIndex);//=>0
console.log(reg.exec(str));//=>['2018']
console.log(reg.lastIndex);//=>11
console.log(reg.exec(str));//=>['2019']
console.log(reg.lastIndex);//=>21
console.log(reg.exec(str));//=>null
console.log(reg.lastIndex);//=>0
console.log(reg.exec(str));//=>['2018']
复制代码
let str="zzzzzzz2018";
let reg=/\d+/g;
console.log(reg.exec(str));//=>["2018"],把reg的内置lastIndex值该为11
console.log(reg.exec("zzzzzzz2018sssssss2019"));//=>["2019"],虽然捕获的不是同一个字符串,可是正则是同一个,上一次正则处理的时候修改了它的lastIndex,也会对下一次匹配新的字符串产生影响
复制代码

基于test进行匹配的时候,若是设置了g,test匹配也至关于捕获,修改了lastIndex的值

let str = 'zzzzzzz2018ssssss2019';
let reg = /\d+/g;
console.log(reg.test(str));//=>TRUE
console.log(reg.lastIndex);//=>11
console.log(reg.exec(str));//=>['2019']

----------------------------
let str = 'zzzzzzz2018';
let reg = /\d+/g;
if(reg.test(str)){//=>此时lastIndex值为11
    console.log(reg.exec(str));//=>NULL
}
复制代码

封装方法--直接捕获全部匹配的内容

//=>相似于String中的match方法
RegExp.prototype.myExecAll=function(str){
	if(!this.global){
		return this.exec(str);
	}
	let ary=[],
		execAll=this.exec(str);
	while(execAll){
		ary.push(execAll[0]);
		execAll=this.exec(str);
	}
	return ary;	
};
复制代码

正则捕获的贪婪性

每一次匹配捕获的时候,老是捕获到和正则匹配中最长的内容

let str="zzzzzzz2018sssssss2019";
let reg=/\d+/g;
console.log(reg.exec(str));//=>["2018"]
复制代码

取消正则捕获的贪婪性

把问号放到量词元字符后面,表明取消捕获的贪婪性

let str="zzzzzzz2018sssssss2019";
let reg=/\d+?/g;
console.log(reg.exec(str));//=>["2"]
复制代码

RegExp.$1

把上一次匹配(TEXT/EXEC)到的结果获取到,获取的是第一个小分组匹配的内容,大正则匹配的内容没法获取,它是一个全局的值,浏览器中$1只有一个,其它的正则操做也会覆盖这个值

let str = 'zzzzzzz20182018sssssss2019';
let reg = /(\d{4})(\1)/g;
console.log(reg.exec(str));//=>["20182018"]
console.log(reg.lastIndex);//=>15
console.log(reg.test(str));//=>TRUE
console.log(RegExp.$1,RegExp.$2);//=>"2018","2018"
复制代码
let str = 'zzzzzzz2018ssssssss2019';
let reg = /(\d+)/g;
console.log(reg.test(str));//=>TRUE
console.log(RegExp.$1);//=>"2018"
复制代码

String中的match方法

match方法用于找到一个或多个正则表达式的匹配

正则不加g返回第一个匹配的便可(至关于reg.exec(str)),加了g,把全部匹配的内容都捕获到,最后统一存储到一个数组中返回

封装--String原型上match方法

String.prototype.myMatch=function myMatch(reg){
	if(!reg.global){
		return reg.exec(this);
	}
	let result=[],
		execAll=reg.exec(this);
	while(execAll){
		result.push(execAll[0]);
		execAll=reg.exec(this);
	}
	return result;
};
复制代码
let reg=/\{(\d+)\}/g;
let reg1=/\{(\d+)\}/;
let str="zzzzzzz{2018}zzzzzzz{2019}zzzzzzz{2020}";
console.log(str.myMatch(reg));
console.log(str.myMatch(reg1));
复制代码

match方法的局限性

在正则设置了g的状况下,基于match捕获的内容只有大正则匹配,小分组的内容没有单独抽取出来(不设置g的状况下和执行exec同样)

let str="zzzzzzz{2018}zzzzzzz{2019}zzzzzzz{2020}zzzzzzz{2021}";
let reg=/\{(\d+)\}/g;

console.log(reg.exec(str));//=>["{2018}","2018"]

console.log(str.match(reg));//=>["{2018}", "{2019}", "{2020}", "{2021}"] 
复制代码

String中的replace方法

语法:[str].replace(reg,[要替换的内容])

用reg正则和str字符串进行匹配,匹配几回就替换几回,每一次都是把当前“大正则”匹配的结果用第二个传递的字符串替换掉了

let str="zzzzzzz{val:2018}zzzzzzz{val:2019}",
	reg=/\{val(\d+)\}/g;

str=str.replace(reg,"@");//=>"zzzzzzz@zzzzzzz@"

str=str.replace(reg,"$1");//=>"zzzzzzz2018zzzzzzz2019"

复制代码

replace替换的内容为函数

reg和str匹配多少次,函数就被触发执行多少次

每一次正则匹配到结果,都把函数执行,而后基于exec把本次匹配的信息捕获到,而后把捕获的信息也就是exec获得的数组中的每一项依次传参给传递给函数

每一次函数中返回值是什么,就把当前大正则匹配的内容替换成啥

let str="zzzzzzz2018zzzzzzz2019";
let reg=/\d+/g;
str=str.replace(reg,(...arg)=>{
	console.log(arg);
	return "AA";
})
console.log(str);//=>"zzzzzzzAAzzzzzzzAA"
复制代码

案例

把"54689"转换为"伍肆陆捌玖"

let str="54389",
	ary=['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
str=str.replace(/\d/g,item=>{//=>item指的是exec中的第一项
	return ary[item];
})
console.log(str);
复制代码

案例

一个url 后面好多key-value 如localhost?key=val&key2=val2&key3=val3 封装一个函数 getParam(‘key’) 经过key得到相应等号后面的值.

function getParam(url,key){
	let obj={};
	url.replace(/([^?#&]+)=([^?#&]+)/g,(...arg)=>{
		[,attr,value]=arg;
		obj[attr]=value;
	});
	return obj[key];
}
复制代码

String中的split方法

字符串根据符合正则的字符进行分割,并用一个数组将分割后的内容返回

split方法不会将分隔符返回,可是会将小分组的内容返回,也就是捕获到的内容

let str="2018/4/30 17:50:23",
	ary=str.split(/\/| |:/g);//=>按照/,空格,:进行拆分
console.log(ary);//=>["2018", "4", "30", "17", "50", "23"]

-------------------
let str = "2018/4/30 17:50:23",
    ary = str.split(/(\/| |:)/g);
console.log(ary);// ["2018", "/", "4", "/", "30", " ", "17", ":", "50", ":", "23"]

----------------------
let str = "2018/4/30 17:50:23",
    ary = str.split(/(?:\/| |:)/g);
console.log(ary);//=>["2018", "4", "30", "17", "50", "23"]
复制代码

时间字符串格式化

let str="2018-4-3 17:34:56";
let ary=str.match(/\d+/g).map(item=>item<10?item="0"+item:item);

console.log(ary)//=>["2018","04,"03","17","34","56"]
复制代码

封装

String.prototype.myFormatTime=function(template="{0}年{1}月{2}日 {3}时{4}分{5}秒"){
	let ary=this.match(/\d+/g).map(function(item){
		return item<10?"0"+item:item;
	})
	template=template.replace(/\{(\d)\}/g,function(...arg){
		return ary[arg[1]] || "00"
	})
	return template;
};

let str="2018-4-3 21:23:45";
str.myFormatTime();//=>"2018年04月03日 21时23分45秒";

let str="2018-4-3";
str.myFormatTime({1}-{2} {3}:{4});//"04-03 00:00"
复制代码

解析URL

function queryParameter(url){
	let obj={};
	url.indexof("#")>0?url=url.replace(/#/g,"&HASH="):null;
	let ary=url.split(/\?/g)[1].split(/=|&/g);
	ary.forEach((item,index)=>{
		if(index%2===1) return;
		obj[item]=ary[index+1];
	})
	ary=null;
	return obj;
};

复制代码

获取一个字符串中最多出现字符的次数和对应的字符

let str = 'zzzzzzzzzzzzzz';
 str=str.split("").sort().join("");
 let ary=str.match(/(\w)\1*/g);
 let obj={};
 ary.forEach((item,index)=>{
	 obj[item[0]]=item.length;
 })
 let max=0;
 for(let key in obj){
	 if(!obj.hasOwnProperty(key)) break;
	 obj[key]>max?max=obj[key]:null;
 }
 for(let key in obj){
	 if(!obj.hasOwnProperty(key)) break;
	 if(obj[key]===max){
		 console.log(`${key}:${max}`);
	 }
 }
复制代码

分组引用

\1表示反向引用,它表示的是第一个括号内的子正则表达式匹配内容,而不是第一个括号的内容

let reg=/(\w)\1/;
let str="zzzzzzz";
console.log(reg.test(str));//=>false
console.log(reg.exec(str));//=>null

复制代码

反向引用只能引用已经捕获到的

let reg=/^(?:b|c)\1/;
let str="bbc.zzzzzzz.cn";
console.log(reg.exec(str));//=>null
复制代码

案例:有效数字

1.能够出现+/-号:能够没有,也能够有一个 2.整数,一位或者多位数字,一位0~9,多位数字不能以0开头 3.小数部分:有可能有,也有可能没有,有小数点后面至少要跟一位数字

let reg=/^[+-]?(\d|[1-9]\d+)(\.\d+)?$/;
复制代码

案例:有效邮箱

第一部分:数字、字母、下划线、-.,可是-.不能做为开头

第二部分:xxx.xx.xx xxx.xx xxx.xx.xx.xx xxx-xxx-xx.xx.xx

let reg=/^\w+((-\w+)|(.\w+))@[A-Za-z0-9)]+([-.][A-Za-z0-9]+)*(\.[A-Za-z0-9]+)$/;
复制代码

案例:中文姓名

中文汉字 [\u4E00-\u9FA5]

let reg=/^[\u4E00-\u9FA5]{2,}(·[\u4E00-\u9FA5]{2,})?$/
复制代码

JS盒子模型属性

在JS中经过相关的属性能够获取(设置)元素的样式信息,这些属性就是盒子模型属性(基本上都是有关样式的)

在JS盒子模型13个属性中,只有scrollTop/scrollLeft是能够写的,其余属性只能读

clientWidth/clientHeight

获取当前元素可视区域的宽高和内容,当咱们设置了固定的宽高,和 是否有溢出无关(和是否设置了overflow:hidden也无关) 内容的宽高+左右/上下padding

  • content-box::width+paddingLeft+paddingRight
  • border-box:width-borderRight-borderLeft
//=>获取当前页面一屏幕(可视区域的宽度和高度)
document.documentElement.clientWidth||document.body.clientWidth(兼容各类模式的浏览器)

document.documentElement.clientHeight||document.body.clientHeight(兼容各类模式的浏览器)
复制代码

clientTop/clientLeft

获取(上/左)边框的宽度

offsetWidth/offsetHeight

在client的基础上加上左右/上下boder

  • content-box:width+paddingLeft+paddingRight+borderRight+borderLeft

  • border-box:width

offsetParent

当前盒子的父级参照物

参照物:同一个平面中,元素的父级参照物和结构没有必然的联系,默认他们的父级参照物都是body(当前平面最外层的盒子),body的父级参照物为null

参照物能够改变:构建出不一样的平面便可(使用z-index,可是这个属性只对定位有做用),改变元素的定位(position:relative/absolute/fixed)能够改变其父级参照物。

若此元素为当前平面的最大元素,那么将往当前元素脱离出来的那个平面上找父级元素

offsetLeft/offsetTop

获取当前盒子距离其父级参照物的偏移量(上偏移量/左偏移)

当前盒子的外边框开始~父级参照物的内边框

案例

无论当前元素的父级元素是谁,都要得到当前元素距离body的左,上偏移量

let offset=function (curEle){
	let curEleLeft=curEle.offsetLeft,
		curEleTop=curEle.offsetTop,
		p=curEle.offsetParent;
	while(p.tagName!=="BODY"){
		curEleLeft+=p.offsetLeft+p.clientLeft;
		curEleTop+=p.offsetTop+p.clientTop;
		p=p.offsetParent;
	}
	return {
		top:curEleTop;
		left:curEleLeft;
	}
}
复制代码

scrollWidth/scrollHeight

真实内容的宽高(包含溢出的内容)+左右/上下padding

真实内容的宽高不必定是本身设定的值,由于可能会存在内容溢出,有内容溢出的状况下,须要把溢出的内容也算上,没有内容溢出和client同样

在不一样浏览器中,无论是否设置了overflow:hidden都会对最后的结果产生影响,因此这个值仅仅作参考,属于约等于的值

//=>获取当前页面的真实宽高(包含溢出的部分)
document.documentElement.scrollWidth||document.body.scrollWidth(兼容各类模式的浏览器)
document.documentElement.scrollHeight||document.body.scrollHeight(兼容各类模式的浏览器)
复制代码

scrollLeft/scrollTop

滚动条卷去的宽度和高度

最小卷去值:0 最大卷去值:真实页面的高度减去一屏幕的高度 document.documentElement.scrollHeight-document.documentElement.clientHeight

操做浏览器的盒子模型属性,咱们通常都要写两套,用来兼容各类模式下的浏览器

let windHandle=function (attr,value){
	if(typeof value !=="undefined"){
		document.documentElement[attr]=value;
		document.body[attr]=value;
		return;
	}
	return document.documentElement[attr]||document.body[attr];
}
复制代码

这两个属性是"可读写"属性,能够修改它的值,可是其他11个属性值都是“只读”属性,只能获取不能修改

JS盒模型属性值的特色

1.获取的都是数字不带单位

2.获取的都是整数,不会出现小数(通常都会四舍五入,尤为是获取的偏移量)

3.获取的结果都是复合样式值(好几个元素样式组合在一块儿的值),若是只想获取单一的样式值(例如:只想获取padding),咱们的盒子模型属性就操做不了了(这不能说没有用,真实项目中咱们就是要获取组合的值)

获取元素具体的某个样式值

[元素].style.xxx 操做获取

只能获取全部写在元素行内上的样式(不写在行内上,无论你写没写都获取不到,真实项目中咱们不多会把样式写在行内上)

获取当前元素全部通过浏览器计算的样式

通过计算的样式:只要当前元素能够在页面中呈现(或者浏览器渲染它了),那么它的样式都是被计算过的

  • 无论当前样式写在哪
  • 无论你是否写了(浏览器会给元素设置一些默认样式)

标准浏览器(IE9+)

window.getComputedStyle([元素],[伪类,通常都写null])

经过getComputedStyle方法获得的是一个对象,每个属性名是当前元素全部的css属性名,属性值是css的属性值

getComputedStyle是window这个实例上的一个属性

IE6~8

[元素].currentStyle 获取通过计算的样式

获取当前元素的某一个样式属性值--封装

/* * getCSS:获取某个元素的某个属性值 * * @param * curEle[object]:当前要操做的元素 * attr[string]:当前要操做的样式属性名 * * return * value[string]:当前要获取的元素的某个属性值 */
let getCSS=function (curEle,attr){
	if("getComputedStyle" in window){
		let value=window.getComputedStyle(curEle,null)[attr];
		let reg=/^-?\d+(\.\d+)?(px|rem|em|pt)?$/i;
		reg.test(value)?value=parseFloat(value):null;
	}else{
		throw new Error('您的浏览器版本太低,请升级到最新版本,谢谢配合!!')
	}
	return value;
};
复制代码

细节知识点补充

一、当属性名用变量的话,只能用对象[属性名]的方法

function test(attr){
    var obj={aa:21};
    console.log(obj.attr);  //undefined
    console.log(obj[attr]);  //21
}
test("aa");
复制代码

二、throw是关键字,用来抛出一个用户自定义的异常

当前函数的执行将被中止,throw以后的语句将不会执行

语法:throw expression(要抛出的表达式)

throw "Error2"  //=>抛出一个值为字符串的异常

throw 42  //=>抛出一个值为整数42的异常

throw true  //=>抛出了一个值为true的异常
复制代码

三、:Error是一个类

语法: new Error(message) 构造一个Error的实例

message是人类可阅读的错误描述信息

基于Error类能够建立用于用户自定义的异常

设置当前元素的某一个具体样式的属性值--封装

let setCss=function(curEle,attr,value){
	if(attr==="opacity"){
		curEle.style.opacity=value;
		curEle.style.filter=`alpha(opacity=${value*100})`;
	}
	if(!isNaN(value){
	    let reg=/^width|height|fontsize|((margin|padding)?(top|left|right|bottom)?)$/i;
	    reg.test(value)?value=+"px":null;
	})
	curEle.style[attr]=value;
};
复制代码

批量给元素设置属性以后--封装

let setGroupCss=function (curEle,options){
	for(let attr in options){
		if(!options.hasOwnProperty(attr)) break;
		setCss(curEle,attr,options[attr]);
	}
};
复制代码

for in循环

遍历一个对象中的键值对的,有多少组键值对,咱们就遍历多少次

遍历的时候,先遍历数字属性名(按照从小到大),再遍历字符串属性名(按照书写顺序)

let obj={name:"xxx",age:27,0:0,sex:0,score:100,1:1};

for(let key in obj){
	console.log(key);
	console.log(obj[key]);
}

输出结果:
0 1 name age sex scroe
0 1 "xxx" 27 0 100
复制代码

for in循环只遍历当前对象可枚举(可遍历)的属性

  • 对象的私有属性(本身写的)是可枚举的
  • 对象中浏览器内置的属性是不可枚举的
  • 本身手动在Object类原型上设置的属性也是可枚举的,Object.prototype内置的自己自带的属性时不枚举的
let obj={name:"xxx",age:27,0:0,sex:0,score:100,1:1};
Object.prototype.bbb=10;
for(let attr in obj){
	console.log(attr);
}
输出结果:
0 1 name age sex score bbb


----------------
为了不把手动在原型上添加的属性也遍历出来
for(let attr in obj){
	if(!obj.hasOwnProperty(attr)){
		break;
	}
	
}
手动在原型上添加的属性会最后遍历
复制代码

给元素设置样式--封装

let css=function (...arg){//=>剩余运算符
	let len=arg.length,
		fn=getCss;
	len>=3?fn=setCss:null;
	len===2&&arg[1] instanceof Object?fn=setGroupCss:null;
	return fn(...arg);//=>展开运算符
};
复制代码

案例:跑马灯

原理:将要展现的内容,完整的克隆一份放到其末尾,利用定时器,每隔一段时间将要展现的内容往前移动一段距离,当第一组的内容所有滚动完,此时框的最左边正好展现的是克隆内容的开头,这时让移动的距离变为0,整个内容回从新返回起始位置,实现无缝;

实现JS动画:让要移动的部分没间隔一段时间(最优动画时间是13~17MS)在原有的LEFT值基础上减去步长(向让动画块一些,步长就大一点)

知识点补充--定时器 window.setTimeout([function],[interval])

设置一个定时器,当到达指定时间后执行对应的方法(执行一次定时器就结束了)

window.setInterval([function],[interval])

设置一个定时器,当达到指定时间后执行对应的方法(之后没间隔这么长时间都要从新的执行定时器的方法,直到定时器清除为止,执行不少次)

let n=0;
setInterval(()=>{
	console.log(++n);
},1000)
复制代码

定时器的返回值

定时器的返回值:当咱们设置定时器(无论是setTimeout仍是setInterval),都会有一个返回值,返回值是一个数字,表明当前是在浏览器中设置的第几个定时器(返回的是定时器序号)

setTimeout和setInterval虽然是处理不一样需求的定时器,可是都是浏览器的定时器,因此设置的时候,返回的序号是依次排列的

setInterval设置完成定时器会有一个返回值,无论执行多少次,这个表明序号的返回值不变(设置定时器就有返回值,执行多少次是定时器的处理)

let timer1=setTimeout(function(){},1000);
console.log(timer1);//=>1 表明版本号

let timer2=setInterval(function(){},1000);
console.log(timer2);//=>2 表明版本号

let timer3=setInterval(function(){},1000);
console.log(timer3);//=>3 表明版本号
复制代码

定时器的清除

clearTimeout([定时器的排队序号])

clearInterval([定时器的排队序号])

let t1=setTimeout(function(){
	console.log(t1);//=>1
	clearTimeout(t1);

	t1=null;//=>手动将以前存储序号的变量赋值为null

})
复制代码
<style>
	.wrapper{
		width:300px;
		height:100px;
		margin:20px auto;
		border:1px solid red;
		position:relative;
	}
	.list{
		position:absolute;
		top:0;
		left:0;
	}
	.wrapper li{
		width:100px;
		height:100px;
		line-height:100px;
		text-align:center;
	}
	li:nth-child(even){
		background-color:red;
	}
	li:nth-child(odd){
		background-color:blue;
	}	
</style>

<div class="wrapper">
	<ul class="list clear" id="list">
		<li>1</li>
		<li>2</li>
		<li>3</li>
		<li>4</li>
	</ul>
</div>
复制代码
let listBox=document.getElementById("list");

listBox.innerHTML+=listBox.innerHTML;

utils.css(listBox,"width",utils.css(listBox,"width")*2);

setInterVal(function(){
	let cur=utils.css(listBox,"left");
	cur-=2;
	utils.css(listBox,"left",cur);

	Math.abs(listBox.offsetLeft)===utils.css(listBox,"width")/2?utils.css(listBox,"left",0)
},13)
复制代码

案例:匀速回到顶部

(function (){
	let link=document.getElementById("link");
	//=>当页面在滚动的时候(鼠标滚轮/键盘按键/手动拖动滚动条/基于JS控制页面滚动的时候...),咱们须要随时获取当前页面卷去的高度,若是卷去的高度>一屏幕的高度,让link显示,不然隐藏
	//=>window.onscroll:当页面滚动的时候触发的事件
	window.onscroll=function(){
		let scrollT=document.documentElement.scrollTop,
			clientT=document.documentElement.clientTop;
		if(scrollT>clientT){
			link.style.display="block";
		}else{
			link.style.displey="none"
		}
	};
	let isRun=false;//=>记录当前是否正在运动,默认false没有运动,当点击A标签开启动画的时候,让其变为true,动画结束让其变为false,在它为true的时候,咱们点击A标签不回话再作任何事情,防止重复点击;
	//=>若是重复点击,会屡次执行函数,也就是会设置多个定时器,走的距离就是全部定时器累加的距离,因此速度回愈来愈快
	link.onclick=function(){
		if(isRun){
			return;
		}
		isRun=true;//=>修改的是上级做用域的isRun的值,若是setInterval没有结束,那么isRun的值一直都为true,再点击的时候,函数执行,直接return,下面的都不会执行
		let curT=document.documentElement.scrollTop,
			time=500,
			interval=17,
			step=curT/time*interval;
			
		let timer=setInterval(function(){
			let curTop=document.documentElement.scrollTop;
			if(curTop===0){
				isRun=false;
				clearInterval(timer);
			}
			curTop-=step;
			document.documentElement.scrollTop=curTop;
		}interval);
		
	}
})()
复制代码

JS中的继承

原型继承

让子类的原型指向父类的一个实例

function A(){
	this.x=100;
}
A.prototype={
	constructor:A,
	getX:function(){
		console.log(this.x);
	}
};
function B(){
	this.y=200;
}
B.prototype=new A();
let f=new B();
复制代码

存在的问题

1.子类能够重写父类原型上的方法,致使父类的实例受到影响

2.父类实例私有的属性以及公有的属性都变为子类实例的共有属性

3.没法使用子类原有原型的上的方法(子类的原型被重定向)

Call继承

把父类在子类中做为普通函数执行,让A中的this变为子类的实例,至关于给子类的实例增长一些父类实例的私有属性和方法

function A(){
	this.x=100;
}
A.prototype={
	constructor:A,
	getX:function(){
		console.log(this.x);
	}
};
function B(){//=>this:f
	A.call(this);//=>把A执行,让A中的this变为f
	this.y=200;
}
let f=new B();
复制代码

寄生组合继承

父类实例私有的变为子类实例私有的,父类共有的变为子类共有的 Object.create() Object类上的一个方法 1.建立一个空对象

2.让新建立的空对象的__proto__指向第一个传递进来的对象

let obj={
	name:"哈哈"
};
console.log(Object.create(obj))
复制代码

function A(){
	this.x=100;
}
A.prototype={
	constructor:A,
	getX:function(){
		console.log(this.x);
	}
};
function B(){
	A.call(this);
	this.y=200;
}
B.prototype=Object.create(A.prototype);
//=>至关于把B的原型指向了新建立的空对象,而空对象的__proto__又指向了A的原型
let f=new B();

----------
为何不直接用B.prototype=A.prototype?

//=>太容易重写原型

//B.prototype.xxx=xxx,就会直接修改A的原型,可是使用Object.create的方式,修改B上的原型,B.prototype.xxx=xxx是在给空对象增长或者修改东西,不会影响A原型上的东西
复制代码

ES6中的类和继承

ES6中建立类

ES6中建立的类是由本身标准语法的,基于这种语法建立出来的类只能NEW执行,不能当作普通函数执行

class Fn{//=>这个大括号至关于FN的原型

	constructor(n,m)}{//=>constructor至关于Fn,因此这个括号里就是函数体里面的东西
		let total=null;
		total=n+m;
		this.x=n;
		this.y=m;
		return total;
	}

	//=>给Fn的原型上设置方法(只能设置方法不能设置属性)
	getX(){
		console.log(this.x);
	}

	//=>把Fn当作一个普通对象设置的Fn类上的私有方法(只能设置方法不能设置属性)

	static AA(){
		console.log(this.y);
	}
}
Fn.aa=100;
Fn.prototype.bb=200;
let f=new Fn(10,20);
复制代码

基于ES6建立的类的继承

extends实现了原型继承

super();相似于call继承

class A{
	constructor(){
		this.x=100;
	}

	getX(){
		console.log(this.y);
	}
}
A.aa=100;
class B extends A{//=>B的原型
	constructor(){
		super();
		this.y=200;
	}

	getY(){
		console.log(this.x);
	}
}
let f=new B();
复制代码

B的实例既能够调取B原型上的公有属性和方法,也能够调取A上的公有属性和方法

B的实例也拥有A的实例的私有属性

B类既有本身的私有属性,也能够调取A类的私有属性

练习题

class Animal {
    constructor(name = 'John Doe', species = '物种'){
        this.name = name; this.species = species;
    }
    sayHello(){
        console.log('hello',this.name,this.species)
    }
}
class Sheep extends Animal{
    constructor(name = 'Jimmy',species = '羊'){
        super(name, species);
        //=>至关于Animal.call(this,name,species);
    }
    sayHello(){
        console.log(super.sayHello);
        console.log('child'); super.sayHello()//=>super指的是Animal.prototype
    }
}
let sheep = new Sheep('Tom');
sheep.sayHello();

复制代码

定时器

设定一个定时器,而且设定了等待的时间,当到达指定的时间,浏览器会把对应的方法执行

设置定时器会有一个返回值,这个值是一个数字,属于定时器的编号,表明当前是第几个定时器(无论是基于setTimeout仍是setInterval建立定时器,这个编号会累加)

setInterval

可执行屡次的定时器,也被称为轮循定时器,每间隔interval这么长的时间,都会把设定的方法从新执行一次,直到定时器被清除

语法:setInterval([function],[interval])

  • [function]:到达时间后执行的方法(设置定时器的时候方法没有执行,到时间浏览器帮咱们执行)
  • [interval]:时间因子(须要等待的时间 MS)

setTimeout

只能执行一次的定时器

语法:setTimeout([function],[interval])

  • [function]:到达时间后执行的方法(设置定时器的时候方法没有执行,到时间浏览器帮咱们执行)
  • [interval]:时间因子(须要等待的时间 MS)

定时器时间因子即便设置为0也不是当即实行,仍是异步编程,每个浏览器都有一个本身最小的等待和反应时间(谷歌:5~6,IE:10~14)

let n=0;
setTimeout(()=>{
	console.log(++n);
},0);
console.log(n);

//=>结果:0,1
复制代码

清除定时器

clearTimeout

均可以清除setInterval和setTimeout建立的定时器,本质是根据序号清除浏览器中设定的定时器

clearInterval

均可以清除setInterval和setTimeout建立的定时器,本质是根据序号清除浏览器中设定的定时器

同步编程&&异步编程

JS中大部分都是同步编程

同步编程

任务是按照顺序依次处理,当前这件事没有完全作完,下一件事是执行不了的

异步编程

定义:当前这件事没有完全作完,须要等待一段时间才能继续处理,此时咱们不等,继续执行下面的任务,当后面的任务完成后,再去把没有完全完成的事情完成

JS中经常使用的异步编程

  • 全部的事件绑定都是异步编程
  • 全部的定时器都是异步编程

定时器到达必定时间,也不必定执行

let n=0;
setInterval(()=>{
	console.log(n);
},1000)
console.log(n);
while(1===1){
//=>死循环
}
复制代码
  • AJAX中通常都使用异步编程处理
  • 回调函数也算是异步编程

浏览器规划同步异步的机制

浏览器是多线程的,JS是单线程的(浏览器只给JS执行分配一个线程)

单线程的特色就是一次只能处理一件事情

进程

每一个应用程序均可以理解为一个进程,浏览器打开一个页面,就至关于开辟一个进程,在一个进程中,咱们须要同时干不少事情,此时咱们能够分配多个线程去同时完成多项任务

JS在单线程中实现异步的机制

主要依赖于浏览器的任务队列完成的,浏览器中有两个任务队列,主任务队列用于供JS代码自上而下执行,等待任务队列用于存放异步操做任务

当主任务队列完成后才会到等待任务队列中进行查找,主任务队列完不成,无论等待任务队列中是否有到达时间的,都不处理,继续等待主任务队列完成。

等待任务队列中谁达到条件了(若是又不少都达到条件了,谁先达到的,就先处理谁),就把这个任务从新放到主任务队列中去执行,把这个任务执行完,再去等待中找下一个,以此类推

setTimeout(() => {
    console.log(1);
}, 20);
console.log(2);
setTimeout(() => {
    console.log(3);
}, 10);
setTimeout(() => {
    console.log(4);
}, 100);
for (let i = 0; i < 90000000; i++) {

}
console.log(5);
复制代码

setTimeout(() => {
    console.log(1);
}, 20);
console.log(2);
setTimeout(() => {
    console.log(3);
}, 10);
console.log(4);
for (let i = 0; i < 90000000; i++) {

}
console.log(5);
setTimeout(() => {
    console.log(6);
}, 8);
console.log(7);
setTimeout(() => {
    console.log(8);
}, 15);
console.log(9);
----
循环以前的定时器,在循环尚未结束的时候时间就已经到了,而循环以后的定时器,是要等到循环结束了,才被放到等到任务序列中,也就是与循环以前的相比要完了200MS(循环的时间),因此即便下面的定时器的时间短,也是上面的定时器先到达时间,先被拿到主任务队列内执行
复制代码

测试程序执行的时间

console.time()、console.timeEnd()

这两个方法配合使用能够用来统计执行某个函数所用的时间

  • console.time()表示计时开始
  • console.timeEnd()表示计时结束,
console.time('AA');
for (var i = 0; i < 90000; i++) {
    for (var j = 0; j < 90000; j++) {}
}
console.timeEnd('AA');
复制代码

利用new Date()

获取当前客户端本机时间(当前获取的时间不能做为重要的参考依据)

let a=new Date();
for (var i = 0; i < 90000; i++) {
    for (var j = 0; j < 90000; j++) {}
}
console.log(new Date()-a);
复制代码

var time = new Date();//=>获取当前客户端本机时间(当前获取的时间不能做为重要的参考依据)

//=>获取的结果是一个日期格式的对象:Sun Apr 01 2018 15:55:23 GMT+0800 (中国标准时间)

typeof new Date() ->'object'

//=>time.getFullYear() 获取四位整数年
//=>time.getMonth() 获取月(0-11表明1-12月)
//=>time.getDate() 获取日
//=>time.getDay() 获取星期(0-6表明周日到周六)
//=>time.getHours() 获取小时
//=>time.getMinutes() 获取分钟
//=>time.getSeconds() 获取秒
//=>time.getMilliseconds()获取毫秒
//=>time.gettime() 获取当前日期距离'1970-1-1 00:00:00'的毫秒差
复制代码
var time = new Date('2017-10-22');//=>当newDate中传递一个时间格式的字符串,至关于把这个字符串转换为标准的时间对象格式(转换完成后,就能够调取上面咱们将的那些方法了)

//=>时间格式的字符串
'2017-10-22'  (IE下识别不了)
'2017/10/22'
'2017/10/22 16:15:34'
1522569836707 (若是传递的是距离1970年的那个毫秒差,也是能够识别转换的,可是这个只能是数字,不能是字符串)

...
复制代码

案例:京东倒计时抢购

let timeBox=document.querySelector(".timeBox"),
	timeSpan=timeBox.querySelector("span"),
	_serverTime=null,
	autoTimer=null;
//=>获取服务器当前的时间,注意缩短客户端接收服务器端的时间
let queryTime=function (){
	return new Promise((resolve)=>{
		let xhr=new XMLHttpRequest();
		xhr.open("head","temp.json",true);//=>"head"的方式会比"get"快
		xhr.onreadystatechange=function(){
			if(xhr.readyState===2 && xhr.status===200){//=>当readystate为2的时候,响应头信息就已经返回了,无需等到4,另外就算请求没有成功也能够获得响应头的信息,可是项目中通常都要加上这句话避免出现404页面
				_serverTime=new Date(xhr.getResponseHeader("date"));//=>请求头上获得的时间信息是格林尼治时间,要用new Date转化成北京时间
				resolve(_serverTime);
			}
		}
		xhr.send(null);
	})
}
//=>动态计算当前倒计时时间
let count=0;//=>记录第几回执行computedTime,由于第一次执行的时候就直接用服务器拿到的时间,第二次以后执行的时候才须要往上累加时间
let computedTime=function (){
	if(count>0){
		let time=_serverTime.getTime();//=>获得距离1970/1/1 00:00:00的时间,再加上1s,再转换回标准北京时间
		time+=1000;
		_serverTime=new Date(time);
	}
	count++;
	let nowTime=_serverTime,
		targetTime=new Date("2018/5/25 23:00:00"),
		changeTime=targetTime-nowTime;//=>两个标准时间相减,获得的是毫秒差
	if(changeTime<=0){
		clearInterval(autoTimer);
	}
	let hours=Math.floor(changeTime/(1000*60*60));
		changeTime=changeTime-hours*60*60*1000;
	let minutes=Math.floor(changeTime/(1000*60));
		changeTime=changeTime-minutes*60*1000;
	let seconds=Math.floor(changeTime/1000);
		hours<10?hours="0"+hours:null;
		minutes<10?minutes="0"+minutes:null;
		seconds<10?seconds="0"+seconds:null;
	timeSpan.innerText=` ${hours}:${minutes}:${seconds} `;
}
queryTime().then(()=>{
	computedTime();
	autoTimer=setInterval(computedTime,1000);
})
//=>若是每次都向服务器发送请求得到当前服务器的时间,会形成服务器崩溃,因此在不刷新页面的状况下,咱们就向服务器请求一次数据,而后经过定时器手动累加时间,只有当页面从新刷新的时候才会从新向服务器发送请求获得时间
复制代码

Promise

Promise是ES6中新增的类(new Promise),目的是为了管理JS中的异步编程,也将其称为"Promise设计模式"

Promsie自己是同步的,能够管理异步操做,new Promise会建立一个Promise实例,当即会把当前函数体中的异步操做执行,必须传递一个回调函数进去,不传递会报错

new Promise(()=>{
	setTimeout(() => {
        console.log(3);//=>最后输出3
    }, 2000);
    console.log(1);//=>先输出1
})
console.log(2);//=>再输出2
复制代码

new Promise的回调函数中会传两个参数(resolve,reject),异步操做执行成功,执行resolve方法;异步操做失败了,执行reject方法

Promise原型上有then方法,第一个参数表明是异步执行成功要作的事情(即resolve),第二个参数表明的是异步执行失败要作的事情(即reject)

new Promise((resolve,reject)=>{
	setTimeout(()=>{
		resolve(100);
	},1000);
}).then((res)=>{
	console.log("ok",res);
},()=>{
	console.log("no");
})
复制代码

then中的对应的方法执行后返回的也是Promise实例(是新的实例,与以前的实例不是一个)能够继续调用then方法,并且第一个then中的回调函数执行的返回结果,会当作实参传给第二个then中对应的回调函数的形参

let pro = new Promise((resolve, reject) => {
    //=>执行一个异步操做
    let xhr = new XMLHttpRequest();
    xhr.open('get', 'js/1.js', true);
    xhr.onreadystatechange = () => {
        if (xhr.readyState === 4 && xhr.status === 200) {
            val = xhr.responseText;
            resolve(val);
        }
        if (xhr.status !== 200) {
            //=>失败
            reject();
        }
    };
    xhr.send(null);
});
pro.then((res)=>{//=>至关于这个函数就是resolve
	console.log(res);//=>输出val,
},()=>{//=>至关于这个函数是reject
	console.log("no");
}).then((res)=>{
	console.log(res);//=>100
},()=>{})
复制代码

若是调用多个then,异步操做成功或者失败,先把第一个then中对应的成功或者失败的方法执行,返回一个新的Promise实例,这个新的实例管控第一个then中方法执行的成功仍是失败,若是成功了就执行第二个then中成功的方法执行,若是失败了就执行了第二个then中失败的方法,相似于树状图

let promise1 = new Promise((resolve, reject) => {
    $.ajax({
        url: 'json/data2.json',
        success(result) {
            resolve(result);
        },
        error(msg) {
            reject('no');
        }
    });
});
promise1.then(
    result => {
        console.log('THEN1 OK', result);
        return 100;
    },
    msg => {
        console.log('THEN1 NO', msg);
        return 100;
    }
).then(
    result => {
        console.log('THEN2 OK', result);
    },
    msg => {
        console.log('THEN2 NO', msg);
    }
);
//=>若'json/data2.json'存在,输出'THEN1 OK 获取的数据',再输出'THEN2 OK 100'
//=>若'json/data2.json'不存在,输出'THEN1 NO xhr的实例',再输出'THEN2 OK 100'(jQuery的ajax中若请求失败,会把xhr实例做为实参传递给失败时执行的函数)

复制代码

真实项目中,通常咱们不用then同时管理成功和失败的方法,只管理成功的方法,失败的方法由catch方法来管理,catch方法不只异步请求失败会执行它,第一个then方法执行失败也会执行它,也就是管着兄弟和爹

let promise1 = new Promise((resolve, reject) => {
    $.ajax({
        url: 'json/data2.json',//=>不存在
        success(result) {
            resolve(result);
        },
        error(msg) {
            reject('no');
        }
    });
});
promise1.then(result => {
    console.log('THEN1 OK', result);
    return 100;
}).catch(msg => {
    console.log('CATCH1', msg);
    return 100;
})
//=>输出"catch1 no"
复制代码
let promise1 = new Promise((resolve, reject) => {
    $.ajax({
        url: 'json/data.json',//=>存在
        success(result) {
            resolve(result);
        },
        error(msg) {
            reject('no');
        }
    });
});
promise1.then(result => {
    console.log('THEN1 OK', result);
    100();
    return 100;
}).catch(msg => {
    console.log('CATCH1', msg);
    return 100;
})
"catch1 TypeError: 100 is not a function"
复制代码
let promise1 = new Promise((resolve, reject) => {
    $.ajax({
        url: 'json/data2.json',//=>不存在
        success(result) {
            resolve(result);
        },
        error(msg) {
            reject('no');
        }
    });
});
promise1.then(result => {
    console.log('THEN1 OK', result);
    return 100;
}).catch(msg => {
    console.log('CATCH1', msg);
    return 100;
}).then(result => {
    console.log('THEN2 OK', result);
    100();
}).catch(msg => {
    console.log('CATCH2', msg);
});
//=>输出'CATCH1 no',
//=>'THEN2 OK 100'
//=>'CATCH2 TypeError: 100 is not a function'
复制代码
let promise1 = new Promise((resolve, reject) => {
    $.ajax({
        url: 'json/data2.json',//=>不存在
        success(result) {
            resolve(result);
        },
        error(msg) {
            reject('no');
        }
    });
});
promise1.then(result => {
    console.log('THEN1 OK', result);
    return 100;
}).catch(msg => {
    console.log('CATCH1', msg);
    100();
    return 100;
}).then(result => {
    console.log('THEN2 OK', result);
}).catch(msg => {
    console.log('CATCH2', msg);
});
//=>输出'CATCH1 no'
//=>'CATCH2 TypeError: 100 is not a function'
复制代码

catch方法和then方法中的回调函数执行的过程当中若是报错浏览器不会报错,会把报错的内容当作实参传递给下一个then或者catch方法,即便当前的catch和then有return值也会被报错覆盖,把报错的内容传递给下一个then或者catch方法,相似于JS中的异常捕获(目的:把抛出异常的错误捕获到,不让其阻断浏览器的继续执行)

try{
	1();
}catch (e){
	//=>try中的代码报错了会执行catch
	console.log(e.message);
} finally{
//=>无论try中的代码成功仍是失败都会执行
	console.log("ok");
}
复制代码

Promise上还有finally方法,无论上面的then或者catch方法执行成功或者失败都会执行finally,可是上面的then或者catch方法的返回值不会传递给finally做为实参,若是上面的then或者catch方法中的回调函数中有报错的话,会直接在浏览器内报错,可是即便这样也不会阻止finally的执行

若是finally方法后面还有then或者catch的话,就当作有没finally方法,管控的仍是前面的then和catch

let promise1 = new Promise((resolve, reject) => {
    $.ajax({
        url: 'json/data.json',//=>地址存在
        success(result) {
            resolve(result);
        },
        error: reject

    });
});
promise1.then(result => {
    console.log('THEN1 OK', result);
    100();
    return 100;
}).catch(msg => {
    console.log('CATCH1', msg);
    100();
    return 100;
}).finally(result => {
    console.log('THEN2 OK', result);
    // 100();
}).then(result=>{
    console.log("ok",result);
}).catch(result=>{
    console.log("no",result);
});

//=>THEN1 OK []
//=>CATCH1 TypeError: 100 is not a function
//=>THEN2 OK undefined
//=>no TypeError: 100 is not a function
复制代码

使用Promise解决回调地狱

有A,B,C三个请求,实现,A请求成功拿到数据后再执行B请求拿到数据后再执行C请求

let promise1=function (){
	return new Promise(reslove){
		$.ajax({
			url:"url1",
			method:"get",
			cache:false
			success:resolve
		})
	}
};
let promise2=function (){
	return new Promise(reslove){
		$.ajax({
			url:"url2",
			method:"get",
			cache:false
			success:resolve
		})
	}
};
let promise3=function (){
	return new Promise(reslove){
		$.ajax({
			url:"url3",
			method:"get",
			cache:false
			success:resolve
		})
	}
}
promise1().then(()=>{
	return promise2();
}).then(()=>{
	promise3();
})
复制代码

回调函数

定义: 把一个函数A当作实参传递给另一个函数B,在B方法执行的时候,把A执行了,这种机制被称为"回调函数机制"


一、能够给回调函数传递实参执行

二、能够改变回调函数中的this指向

三、能够在宿主函数(它在哪执行的,它的宿主函数就是谁)中接收回调函数执行的返回结果

let fn=(callback)=>{
	callback&&callback.call(obj,100,200);
	let res=callback(10,20);
	console.log(res);
}
复制代码

回调函数中的this通常是window,除非宿主函数执行回调函数的时候把this特殊指向了(箭头函数除外:箭头函数中的this是它的上下文)

重写jQuery上的each方法

能够遍历数组,类数组,对象,若是是数组(类数组)和forEach方法相似,若是是对象和for in循环相似

let each=function(obj,callback){
	if(obj.hasOwnProperty("length")){
		for(let i=0;i<obj.length;i++){
			let res=callback&&callback.call(obj[i],i,obj[i]);
			if(res===false) break;
		}else{
			for(let key in obj){
				if(!obj.hasOwnProperty(key)) break;
				let res=callback&&callback.call(obj[key],key,obj[key]);
				if(res===false) break;
			}
		}
	}
}
复制代码

重写forEach方法

循环遍历数组中的每一项,每遍历一次把回调函数执行一次,并把当前项和对应的索引传递给回调函数,若是方法中传递第二个参数的话,那么将回调函数中的this指向第二个参数

Array.prototype.myforEach=function myforEach(callback,context){
	context=context||window;
	for(let i=0;i<this.length;i++){
		callback.call(context,this[i],i);
	}
};
复制代码

重写map方法

循环遍历数组中的每一项,每遍历一次把回调函数执行一次,并把当前项和对应的索引传递给回调函数,并将当前项替换成回调函数的返回值,循环结束后以新数组的形式返回,若是方法中传递第二个参数的话,那么将回调函数中的this指向第二个参数

Array.prototype.myMap=function myMap(callback,context){
	context=context||window;
	let ary=[];
	for(let i=0;i<this.length;i++){
		let res=callback&&callback.call(context,this[i],i);	
		ary.push(res);
	}
	return ary;
}
复制代码

重写filter方法

循环遍历数组中的每一项,每遍历一次把回调函数执行一次,并把当前项和对应的索引传递给回调函数,将全部回调函数返回值(Boolean后)为true的当前项以新数组的形式返回,若是方法中传递第二个参数的话,那么将回调函数中的this指向第二个参数

Array.prototype.myFilter=function myFilter(callback,context){
	context=context||window;
	let ary=[];
	for(let i=0;i<this.length;i++){
		let res=callback&&callback.call(context,this[i],i);
		if(res){
			ary.push(this[i]);
		}
	}
	return ary;
}
复制代码

重写find方法

循环遍历数组中的每一项,每遍历一次把回调函数执行一次,并把当前项和对应的索引传递给回调函数,将全部回调函数返回值(Boolean后)为ture的当前项返回,只要找到一个返回值为true的就再也不往下找了

Array.prototype.myFind=function myFind(callback){
	for(let i=0;i<this.length;i++){
		let res=callback&&callback(this[i],i);
		if(res){
			return this[i];
		}
	}
}
复制代码

重写replace方法

重写replace方法,第一个参数替换成第二个参数,若是第一个参数是正则,第二个参数是回调函数,正则每匹配一次就把回调函数执行一次,并把exec捕获到的数组中的每一项依次传递给回调函数,回调函数的返回结果会替换被匹配到的内容,原有字符串不变

String.prototype.myReplace=function(reg,callback){
    let str=this;
    if(typeof reg==="string"&&typeof callback==="string"){
        if(str.indexOf(reg)<0) return str;
        let a=str.indexOf(reg[0]),
            b=str.indexOf(reg[reg.length-1]);
        str=str.substring(0,a)+callback+str.substring(b+1);
    }else if(reg instanceof RegExp&&typeof callback==="function"){
        if(!reg.global){
            let regExec=reg.exec(this);
            let res=callback(...regExec);
            let c=regExec[0],
                a=str.indexOf(c[0]),
                b=str.indexOf(c[c.length-1]);
            str=str.substring(0,a)+res+str.substring(b+1);
            return str;
        }
        let regExec=reg.exec(this);
        while (regExec){
            let res=callback(...regExec);
            let c=regExec[0];
            let a=str.indexOf(c[0]),
                b=str.indexOf(c[c.length-1]);
            str=str.substring(0,a)+res+str.substring(b+1);
            regExec=reg.exec(this);
        }
    }else if(typeof reg==="string"&&typeof callback==="function"){
        let res=callback();
        let a=str.indexOf(reg[0]),
            b=str.indexOf(reg[reg.length-1]);
        str=str.substring(0,a)+res+str.substring(b+1);
    }
    return str;
};
复制代码
相关文章
相关标签/搜索