定义
:是一个用来处理字符串的规则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"]
复制代码
只要在正则中出现的元字符(在基于字面方式建立),除了特殊和有量词意义的之外,其他的都是普通元字符
定义
用于检索字符串中的正则表达式的匹配,并返回包含该查找结果的一个数组。若是能够匹配获取的结果是一个数组,若是不能匹配获取的结果是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盒子模型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个属性值都是“只读”属性,只能获取不能修改
1.获取的都是数字不带单位
2.获取的都是整数,不会出现小数(通常都会四舍五入,尤为是获取的偏移量)
3.获取的结果都是复合样式值(好几个元素样式组合在一块儿的值),若是只想获取单一的样式值(例如:只想获取padding),咱们的盒子模型属性就操做不了了(这不能说没有用,真实项目中咱们就是要获取组合的值)
只能获取全部写在元素行内上的样式(不写在行内上,无论你写没写都获取不到,真实项目中咱们不多会把样式写在行内上)
通过计算的样式:只要当前元素能够在页面中呈现(或者浏览器渲染它了),那么它的样式都是被计算过的
- 无论当前样式写在哪
- 无论你是否写了(浏览器会给元素设置一些默认样式)
标准浏览器(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);
}
})()
复制代码
让子类的原型指向父类的一个实例
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.
没法使用子类原有原型的上的方法(子类的原型被重定向)
把父类在子类中做为普通函数执行,让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中建立的类是由本身标准语法的,基于这种语法建立出来的类只能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);
复制代码
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建立定时器,这个编号会累加)
可执行屡次的定时器,也被称为轮循定时器,每间隔interval这么长的时间,都会把设定的方法从新执行一次,直到定时器被清除
语法
:setInterval([function],[interval])
只能执行一次的定时器
语法
:setTimeout([function],[interval])
定时器时间因子即便设置为0也不是当即实行,仍是异步编程,每个浏览器都有一个本身最小的等待和反应时间(谷歌:5~6,IE:10~14)
let n=0;
setTimeout(()=>{
console.log(++n);
},0);
console.log(n);
//=>结果:0,1
复制代码
均可以清除setInterval和setTimeout建立的定时器,本质是根据序号清除浏览器中设定的定时器
均可以清除setInterval和setTimeout建立的定时器,本质是根据序号清除浏览器中设定的定时器
JS中大部分都是同步编程
任务是按照顺序依次处理,当前这件事没有完全作完,下一件事是执行不了的
定义
:当前这件事没有完全作完,须要等待一段时间才能继续处理,此时咱们不等,继续执行下面的任务,当后面的任务完成后,再去把没有完全完成的事情完成
JS中经常使用的异步编程
定时器到达必定时间,也不必定执行
let n=0;
setInterval(()=>{
console.log(n);
},1000)
console.log(n);
while(1===1){
//=>死循环
}
复制代码
浏览器是多线程的,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('AA');
for (var i = 0; i < 90000; i++) {
for (var j = 0; j < 90000; j++) {}
}
console.timeEnd('AA');
复制代码
获取当前客户端本机时间(当前获取的时间不能做为重要的参考依据)
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是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;
};
复制代码