js:4.

JS第四周
正则
定义:是一个用来处理字符串的规则
正则只能用来处理字符串
处理一般包含两方面
验证当前字符串是否符合某个规则”正则匹配”
把一个字符串中符合规则的字符获取到”正则捕获”
创建正则的两种方式
1.let reg1=/^\d+$/g;//=>字面量方式
2.let reg2=new RegExp("^\\d+$","g");//=>构造函数方式
元字符和修饰符
正则两个斜杠之间包起来的都是”元字符”,斜杠后面出现的都是”修饰符”
常用修饰符
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中的一个数字
1.let reg=/^.$/;
2.//=>一个正则设置了^和$,那么代表的含义其实就是只能是xxx
3.---------------------
4.let reg=/^[.]+$/;
5.console.log(reg.test("n"));//=>false
6.console.log(reg.test("1"));//=>false
7.console.log(reg.test("nn"));//=>false
8.console.log(reg.test("..."));//=>true
9.
10.---------------------
11.let reg=/^[\d]+$/;
12.console.log(reg.test("d"));//=>false
13.console.log(reg.test("0"));//=>true
14.
中括号中出现的两位数,不是两位数,而是两个数字中的任意一个
1.let reg=/^[18]$/;
2.console.log(reg.test("1"));//=>true
3.console.log(reg.test("8"));//=>true
4.console.log(reg.test("18"));//=>false
5.-------------------
6.let reg=/[18]/;
7.console.log(reg.test("18"));//=>true
8.console.log(reg.test("81321"));//=>true
9.console.log(reg.test("22"));//=>false
10.-------------------
11.let reg=/^[12-65]$/;
12.//=>这个正则的意思是,只能是1,5,或者2-6之间的一个数
13.console.log(reg.test("1"));//=>true
14.console.log(reg.test("7"));//=>false
案例
匹配年龄在18~65之间
1./*
2. * 18~19  1[89]
3. * 20~59  [2-5]\d
4. * 60~65  6[0-5]
5. */
6.
7.let reg=/^(1[89])|([2-5]\d)|(6[0-5])$/
案例
验证身份证号,并把生日,地域,性别信息匹配出来
1.//=>前6位代表地域,倒数第二位偶数代表女,奇数代表男
2.
3.let reg=/^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d|x)$/
4.
5.let str="130828199012040617"
6.
7.console.log(reg.exec(str));
8.["130828199012040617","130828","1990","12","04","1",index:0,input:"130828199012040617"]
普通元字符
只要在正则中出现的元字符(在基于字面方式创建),除了特殊和有量词意义的以外,其余的都是普通元字符
正则的EXEC方法(正则捕获)
定义
用于检索字符串中的正则表达式的匹配,并返回包含该查找结果的一个数组。如果可以匹配获取的结果是一个数组,如果不能匹配获取的结果是null
1.let str="zhufeng2018peixun2019";
2.let reg=/\d+/;
3.console.log(reg.exec(str));

如果我们只在匹配的时候,想要获取大正则中部分信息,可以把想要捕获到的内容使用小括号包起来,形成一个分组,这样在捕获的时候,不仅可以把大正则匹配的信息获取到,而且还单独的把小分组匹配的部分信息也捕获到了(分组捕获)
如果使用正则分组不是为了捕获信息,只是为了改变优先级或者进行分组引用,可以在分组的前面加上”?:”,代表只去匹配,但是不把这个分组内容捕获 
正则的捕获有懒惰性
执行一次exec只能捕获到第一个匹配的内容,剩余的默认捕获不到,哪怕是全局下执行一次exec只能捕获一个匹配的内容,在全局下想要获得所有匹配的内容需要不停的执行exec方法,直到返回值是null为止
lastIndex是正则实例上内置的属性,,lastIndex不变导致了正则捕获的懒惰性,不设置全局匹配的情况下,不管怎么操作当前正则实例的lastIndex属性值一直为0
1.let str="zhufeng2018peixun2019";
2.let reg=/\d+/;
3.console.log(reg.exec(str));//=>["2018"]
4.
5.console.log(reg.lastIndex);//=>0
6.console.log(reg.exec(str));//=>["2018"]
7.console.log(reg.lastIndex);//=>0
8.console.log(reg.exec(str));//=>["2018"]
9.console.log(reg.lastIndex);//=>0
10.
11.------
12.???改变后lastIndex已经变了,为什么不按照lastIndex查找的匹配的机制去匹配
13.
14.//=>即使手动改变lastIndex值也没有作用
15.console.log(reg.exec(str));//=>['2018']
16.reg.lastIndex = 11;
17.console.log(reg.lastIndex);//=>11
18.console.log(reg.exec(str));//=>['2018']
19.console.log(reg.exec(str));//=>11
解决正则懒惰性的方案
唯一的解决方法: 需要加全局修饰符G,此时只要多次执行exec方法,就可以匹配到字符串中所有匹配的内容
lastIndex意思是正则表达式开始下一次查找的索引位置,第一次查找完了的时候会把lastIndex的值设为匹配到的字符串的最后一个字符的索引位置加1,第二次查找的时候会从lastIndex这个位置开始,后面以此类推。如果没有找到,则会把lastIndex重置为0。
1.let str = 'zhufeng2018peixun2019';
2.let reg = /\d+/g;
3.console.log(reg.lastIndex);//=>0
4.console.log(reg.exec(str));//=>['2018']
5.console.log(reg.lastIndex);//=>11
6.console.log(reg.exec(str));//=>['2019']
7.console.log(reg.lastIndex);//=>21
8.console.log(reg.exec(str));//=>null
9.console.log(reg.lastIndex);//=>0
10.console.log(reg.exec(str));//=>['2018']
1.let str="zhufeng2018";
2.let reg=/\d+/g;
3.console.log(reg.exec(str));//=>["2018"],把reg的内置lastIndex值该为11
4.console.log(reg.exec("zhufeng2018zhufeng2019"));//=>["2019"],虽然捕获的不是同一个字符串,但是正则是同一个,上一次正则处理的时候修改了它的lastIndex,也会对下一次匹配新的字符串产生影响
基于test进行匹配的时候,如果设置了g,test匹配也相当于捕获,修改了lastIndex的值
1.let str = 'zhufeng2018peixun2019';
2.let reg = /\d+/g;
3.console.log(reg.test(str));//=>TRUE
4.console.log(reg.lastIndex);//=>11
5.console.log(reg.exec(str));//=>['2019']
6.
7.----------------------------
8.let str = 'zhufeng2018';
9.let reg = /\d+/g;
10.if(reg.test(str)){//=>此时lastIndex值为11
11.    console.log(reg.exec(str));//=>NULL
12.}
封装方法–直接捕获所有匹配的内容
1.//=>类似于String中的match方法
2.RegExp.prototype.myExecAll=function(str){
3.    if(!this.global){
4.        return this.exec(str);
5.    }
6.    let ary=[],
7.        execAll=this.exec(str);
8.    while(execAll){
9.        ary.push(execAll[0]);
10.        execAll=this.exec(str);
11.    }
12.    return ary; 
13.};
正则捕获的贪婪性
每一次匹配捕获的时候,总是捕获到和正则匹配中最长的内容
1.let str="zhufeng2018peixun2019";
2.let reg=/\d+/g;
3.console.log(reg.exec(str));//=>["2018"]
取消正则捕获的贪婪性
把问号放到量词元字符后面,代表取消捕获的贪婪性
1.let str="zhufeng2018peixun2019";
2.let reg=/\d+?/g;
3.console.log(reg.exec(str));//=>["2"]
RegExp.$1
把上一次匹配(TEXT/EXEC)到的结果获取到,获取的是第一个小分组匹配的内容,大正则匹配的内容无法获取,它是一个全局的值,浏览器中$1只有一个,其它的正则操作也会覆盖这个值
1.let str = 'zhufeng20182018peixun2019';
2.let reg = /(\d{4})(\1)/g;
3.console.log(reg.exec(str));//=>["20182018"]
4.console.log(reg.lastIndex);//=>15
5.console.log(reg.test(str));//=>TRUE
6.console.log(RegExp.$1,RegExp.$2);//=>"2018"

1.let str = 'zhufeng2018peixun2019';
2.let reg = /(\d+)/g;
3.console.log(reg.test(str));//=>TRUE
4.console.log(RegExp.$1);//=>["2018"]
String中的match方法
match方法用于找到一个或多个正则表达式的匹配
正则不加g返回第一个匹配的即可(相当于reg.exec(str)),加了g,把所有匹配的内容都捕获到,最后统一存储到一个数组中返回
封装–String原型上match方法
1.String.prototype.myMatch=function myMatch(reg){
2.    if(!reg.global){
3.        return reg.exec(this);
4.    }
5.    let result=[],
6.        execAll=reg.exec(this);
7.    while(execAll){
8.        result.push(execAll[0]);
9.        execAll=reg.exec(this);
10.    }
11.    return result;
12.};
1.let reg=/\{(\d+)\}/g;
2.let reg1=/\{(\d+)\}/;
3.let str="zhufeng{2018}zhufeng{2019}zhufeng{2020}";
4.console.log(str.myMatch(reg));
5.console.log(str.myMatch(reg1));
 
match方法的局限性
在正则设置了g的情况下,基于match捕获的内容只有大正则匹配,小分组的内容没有单独抽取出来(不设置g的情况下和执行exec一样)
1.let str="zhufeng{2018}peixun{2019}yangfan{2020}qihang{2021}";
2.let reg=/\{(\d+)\}/g;
3.
4.console.log(reg.exec(str));//=>["{2018}","2018"]
5.
6.console.log(str.match(reg));//=>["{2018}", "{2019}", "{2020}", "{2021}"] 
String中的replace方法
语法:[str].replace(reg,[要替换的内容])
用reg正则和str字符串进行匹配,匹配几次就替换几次,每一次都是把当前“大正则”匹配的结果用第二个传递的字符串替换掉了
1.let str="zhufeng{val:2018}zhufeng{val:2019}",
2.    reg=/\{val(\d+)\}/g;
3.
4.str=str.replace(reg,"@");//=>"zhufeng@zhufeng@"
5.
6.str=str.replace(reg,"$1");//=>"zhufeng2018zhufeng2019"
7.
replace替换的内容为函数
reg和str匹配多少次,函数就被触发执行多少次
每一次正则匹配到结果,都把函数执行,然后基于exec把本次匹配的信息捕获到,然后把捕获的信息也就是exec得到的数组中的每一项依次传参给传递给函数
每一次函数中返回值是什么,就把当前大正则匹配的内容替换成啥
1.let str="zhufeng2018zhufeng2019";
2.let reg=/\d+/g;
3.str=str.replace(reg,(...arg)=>{
4.    console.log(arg);
5.    return "AA";
6.})
7.console.log(str);//=>"zhufengAAzhufengAA"
 
案例
把”54689”转换为”伍肆陆捌玖”
1.let str="54389",
2.    ary=['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
3.str=str.replace(/\d/g,item=>{//=>item指的是exec中的第一项
4.    return ary[item];
5.})
6.console.log(str);
案例
一个url 后面好多key-value 如localhost?key=val&key2=val2&key3=val3 封装一个函数 getParam(‘key’) 通过key获得相应等号后面的值.
1.function getParam(url,key){
2.    let obj={};
3.    url.replace(/([^?#&]+)=([^?#&]+)/g,(...arg)=>{
4.        [,attr,value]=arg;
5.        obj[attr]=value;
6.    });
7.    return obj[key];
8.}
String中的split方法
字符串根据符合正则的字符进行分割,并用一个数组将分割后的内容返回
split方法不会将分隔符返回,但是会将小分组的内容返回,也就是捕获到的内容
1.let str="2018/4/30 17:50:23",
2.    ary=str.split(/\/| |:/g);//=>按照/,空格,:进行拆分
3.console.log(ary);//=>["2018", "4", "30", "17", "50", "23"]
4.
5.-------------------
6.let str = "2018/4/30 17:50:23",
7.    ary = str.split(/(\/| |:)/g);
8.console.log(ary);// ["2018", "/", "4", "/", "30", " ", "17", ":", "50", ":", "23"]
9.
10.----------------------
11.let str = "2018/4/30 17:50:23",
12.    ary = str.split(/(?:\/| |:)/g);
13.console.log(ary);//=>["2018", "4", "30", "17", "50", "23"]
时间字符串格式化
1.let str="2018-4-3 17:34:56";
2.let ary=str.match(/\d+/g).map(item=>item<10?item="0"+item:item);
3.
4.console.log(ary)//=>["2018","04,"03","17","34","56"]
封装
1.String.prototype.myFormatTime=function(template="{0}年{1}月{2}日 {3}时{4}分{5}秒"){
2.    let ary=this.match(/\d+/g).map(function(item){
3.        return item<10?"0"+item:item;
4.    })
5.    template=template.replace(/\{(\d)\}/g,function(...arg){
6.        return ary[arg[1]] || "00"
7.    })
8.    return template;
9.};
10.
11.let str="2018-4-3 21:23:45";
12.str.myFormatTime();//=>"2018年04月03日 21时23分45秒";
13.
14.let str="2018-4-3";
15.str.myFormatTime({1}-{2} {3}:{4});//"04-03 00:00"
解析URL
1.function queryParameter(url){
2.    let obj={};
3.    url.indexof("#")>0?url=url.replace(/#/g,"&HASH="):null;
4.    let ary=url.split(/\?/g)[1].split(/=|&/g);
5.    ary.forEach((item,index)=>{
6.        if(index%2===1) return;
7.        obj[item]=ary[index+1];
8.    })
9.    ary=null;
10.    return obj;
11.};
12.
获取一个字符串中最多出现字符的次数和对应的字符
1. let str = 'zhufengpeixunzhouxiaotianzuishuai';
2. str=str.split("").sort().join("");
3. let ary=str.match(/(\w)\1*/g);
4. let obj={};
5. ary.forEach((item,index)=>{
6.     obj[item[0]]=item.length;
7. })
8. let max=0;
9. for(let key in obj){
10.     if(!obj.hasOwnProperty(key)) break;
11.     obj[key]>max?max=obj[key]:null;
12. }
13. for(let key in obj){
14.     if(!obj.hasOwnProperty(key)) break;
15.     if(obj[key]===max){
16.         console.log(`${key}:${max}`);
17.     }
18. }
分组引用
\1表示反向引用,它表示的是第一个括号内的子正则表达式匹配内容,而不是第一个括号的内容
1.let reg=/(\w)\1/;
2.let str="zhufengpeixun";
3.console.log(reg.test(str));//=>false
4.console.log(reg.exec(str));//=>null
5.
反向引用只能引用已经捕获到的
1.let reg=/^(?:b|c)\1/;
2.let str="bbc.zhufengpeixun.cn";
3.console.log(reg.exec(str));//=>null
案例:有效数字
1.可以出现+/-号:可以没有,也可以有一个 
2.整数,一位或者多位数字,一位0~9,多位数字不能以0开头 
3.小数部分:有可能有,也有可能没有,有小数点后面至少要跟一位数字
1.let reg=/^[+-]?(\d|[1-9]\d+)(\.\d+)?$/;
案例:有效邮箱
第一部分:数字、字母、下划线、-、.,但是-和.不能作为开头
第二部分:xxx.xx.xx xxx.xx xxx.xx.xx.xx xxx-xxx-xx.xx.xx
1.let reg=/^\w+((-\w+)|(.\w+))@[A-Za-z0-9)]+([-.][A-Za-z0-9]+)*(\.[A-Za-z0-9]+)$/;
案例:中文姓名
中文汉字 [\u4E00-\u9FA5]
1.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
1.//=>获取当前页面一屏幕(可视区域的宽度和高度)
2.document.documentElement.clientWidth||document.body.clientWidth(兼容各种模式的浏览器)
3.
4.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的左,上偏移量
1.let offset=function (curEle){
2.    let curEleLeft=curEle.offsetLeft,
3.        curEleTop=curEle.offsetTop,
4.        p=curEle.offsetParent;
5.    while(p.tagName!=="BODY"){
6.        curEleLeft+=p.offsetLeft+p.clientLeft;
7.        curEleTop+=p.offsetTop+p.clientTop;
8.        p=p.offsetParent;
9.    }
10.    return {
11.        top:curEleTop;
12.        left:curEleLeft;
13.    }
14.}

scrollWidth/scrollHeight
真实内容的宽高(包含溢出的内容)+左右/上下padding
真实内容的宽高不一定是自己设定的值,因为可能会存在内容溢出,有内容溢出的情况下,需要把溢出的内容也算上,没有内容溢出和client一样
在不同浏览器中,不管是否设置了overflow:hidden都会对最后的结果产生影响,所以这个值仅仅做参考,属于约等于的值
1.//=>获取当前页面的真实宽高(包含溢出的部分)
2.document.documentElement.scrollWidth||document.body.scrollWidth(兼容各种模式的浏览器)
3.document.documentElement.scrollHeight||document.body.scrollHeight(兼容各种模式的浏览器)
scrollLeft/scrollTop
滚动条卷去的宽度和高度
最小卷去值:0 
最大卷去值:真实页面的高度减去一屏幕的高度 
document.documentElement.scrollHeight-document.documentElement.clientHeight
操作浏览器的盒子模型属性,我们一般都要写两套,用来兼容各种模式下的浏览器
1.let windHandle=function (attr,value){
2.    if(typeof value !=="undefined"){
3.        document.documentElement[attr]=value;
4.        document.body[attr]=value;
5.        return;
6.    }
7.    return document.documentElement[attr]||document.body[attr];
8.}
这两个属性是”可读写”属性,可以修改它的值,但是其余11个属性值都是“只读”属性,只能获取不能修改
JS盒模型属性值的特点
1.获取的都是数字不带单位
2.获取的都是整数,不会出现小数(一般都会四舍五入,尤其是获取的偏移量)
3.获取的结果都是复合样式值(好几个元素样式组合在一起的值),如果只想获取单一的样式值(例如:只想获取padding),我们的盒子模型属性就操作不了了(这不能说没有用,真实项目中我们就是要获取组合的值)
获取元素具体的某个样式值
[元素].style.xxx 操作获取
只能获取所有写在元素行内上的样式(不写在行内上,不管你写没写都获取不到,真实项目中我们很少会把样式写在行内上)
获取当前元素所有经过浏览器计算的样式
经过计算的样式:只要当前元素可以在页面中呈现(或者浏览器渲染它了),那么它的样式都是被计算过的
不管当前样式写在哪
不管你是否写了(浏览器会给元素设置一些默认样式)
标准浏览器(IE9+)
window.getComputedStyle([元素],[伪类,一般都写null])
通过getComputedStyle方法得到的是一个对象,每一个属性名是当前元素所有的css属性名,属性值是css的属性值
getComputedStyle是window这个实例上的一个属性
IE6~8
[元素].currentStyle 获取经过计算的样式
获取当前元素的某一个样式属性值--封装
1./*
2. * getCSS:获取某个元素的某个属性值
3. * 
4. * @param
5. *   curEle[object]:当前要操作的元素
6. *   attr[string]:当前要操作的样式属性名
7. *  
8. *  return 
9. *     value[string]:当前要获取的元素的某个属性值
10. */
11.let getCSS=function (curEle,attr){
12.    if("getComputedStyle" in window){
13.        let value=window.getComputedStyle(curEle,null)[attr];
14.        let reg=/^-?\d+(\.\d+)?(px|rem|em|pt)?$/i;
15.        reg.test(value)?value=parseFloat(value):null;
16.    }else{
17.        throw new Error('您的浏览器版本过低,请升级到最新版本,谢谢配合!!')
18.    }
19.    return value;
20.};
细节知识点补充
1、当属性名用变量的话,只能用对象[属性名]的方法
1.function test(attr){
2.    var obj={aa:21};
3.    console.log(obj.attr);  //undefined
4.    console.log(obj[attr]);  //21
5.}
6.test("aa");
2、throw是关键字,用来抛出一个用户自定义的异常
当前函数的执行将被停止,throw之后的语句将不会执行
语法:throw expression(要抛出的表达式)
1.throw "Error2"  //=>抛出一个值为字符串的异常
2.
3.throw 42  //=>抛出一个值为整数42的异常
4.
5.throw true  //=>抛出了一个值为true的异常
3、:Error是一个类
语法: new Error(message) 构造一个Error的实例
message是人类可阅读的错误描述信息
基于Error类可以创建用于用户自定义的异常
设置当前元素的某一个具体样式的属性值--封装
1.let setCss=function(curEle,attr,value){
2.    if(attr==="opacity"){
3.        curEle.style.opacity=value;
4.        curEle.style.filter=`alpha(opacity=${value*100})`;
5.    }
6.    if(!isNaN(value){
7.        let reg=/^width|height|fontsize|((margin|padding)?(top|left|right|bottom)?)$/i;
8.        reg.test(value)?value=+"px":null;
9.    })
10.    curEle.style[attr]=value;
11.};
批量给元素设置属性之后--封装
1.let setGroupCss=function (curEle,options){
2.    for(let attr in options){
3.        if(!options.hasOwnProperty(attr)) break;
4.        setCss(curEle,attr,options[attr]);
5.    }
6.};
for in循环
遍历一个对象中的键值对的,有多少组键值对,我们就遍历多少次
遍历的时候,先遍历数字属性名(按照从小到大),再遍历字符串属性名(按照书写顺序)
1.let obj={name:"xxx",age:27,0:0,sex:0,score:100,1:1};
2.
3.for(let key in obj){
4.    console.log(key);
5.    console.log(obj[key]);
6.}
7.
8.输出结果:
9.0 1 name age sex scroe
10.0 1 "xxx" 27 0 100
for in循环只遍历当前对象可枚举(可遍历)的属性
对象的私有属性(自己写的)是可枚举的
对象中浏览器内置的属性是不可枚举的
自己手动在Object类原型上设置的属性也是可枚举的,Object.prototype内置的本身自带的属性时不枚举的
1.let obj={name:"xxx",age:27,0:0,sex:0,score:100,1:1};
2.Object.prototype.bbb=10;
3.for(let attr in obj){
4.    console.log(attr);
5.}
6.输出结果:
7.0 1 name age sex score bbb
8.
9.
10.----------------
11.为了避免把手动在原型上添加的属性也遍历出来
12.for(let attr in obj){
13.    if(!obj.hasOwnProperty(attr)){
14.        break;
15.    }
16.
17.}
18.手动在原型上添加的属性会最后遍历
给元素设置样式–封装
1.let css=function (...arg){//=>剩余运算符
2.    let len=arg.length,
3.        fn=getCss;
4.    len>=3?fn=setCss:null;
5.    len===2&&arg[1] instanceof Object?fn=setGroupCss:null;
6.    return fn(...arg);//=>展开运算符
7.};
案例:跑马灯
原理:将要展示的内容,完整的克隆一份放到其末尾,利用定时器,每隔一段时间将要展示的内容往前移动一段距离,当第一组的内容全部滚动完,此时框的最左边正好展示的是克隆内容的开头,这时让移动的距离变为0,整个内容回重新返回起始位置,实现无缝;

实现JS动画:让要移动的部分没间隔一段时间(最优动画时间是13~17MS)在原有的LEFT值基础上减去步长(向让动画块一些,步长就大一点)
知识点补充--定时器 
window.setTimeout([function],[interval])
设置一个定时器,当到达指定时间后执行对应的方法(执行一次定时器就结束了)
window.setInterval([function],[interval])
设置一个定时器,当达到指定时间后执行对应的方法(以后没间隔这么长时间都要重新的执行定时器的方法,直到定时器清除为止,执行很多次)
1.let n=0;
2.setInterval(()=>{
3.    console.log(++n);
4.},1000)
定时器的返回值
定时器的返回值:当我们设置定时器(不管是setTimeout还是setInterval),都会有一个返回值,返回值是一个数字,代表当前是在浏览器中设置的第几个定时器(返回的是定时器序号)
setTimeout和setInterval虽然是处理不同需求的定时器,但是都是浏览器的定时器,所以设置的时候,返回的序号是依次排列的
setInterval设置完成定时器会有一个返回值,不管执行多少次,这个代表序号的返回值不变(设置定时器就有返回值,执行多少次是定时器的处理)
1.let timer1=setTimeout(function(){},1000);
2.console.log(timer1);//=>1  代表版本号
3.
4.let timer2=setInterval(function(){},1000);
5.console.log(timer2);//=>2  代表版本号
6.
7.let timer3=setInterval(function(){},1000);
8.console.log(timer3);//=>3  代表版本号
定时器的清除
clearTimeout([定时器的排队序号])
clearInterval([定时器的排队序号])
1.let t1=setTimeout(function(){
2.    console.log(t1);//=>1
3.    clearTimeout(t1);
4.
5.    t1=null;//=>手动将之前存储序号的变量赋值为null
6.
7.})
1.<style>
2.    .wrapper{
3.        width:300px;
4.        height:100px;
5.        margin:20px auto;
6.        border:1px solid red;
7.        position:relative;
8.    }
9.    .list{
10.        position:absolute;
11.        top:0;
12.        left:0;
13.    }
14.    .wrapper li{
15.        width:100px;
16.        height:100px;
17.        line-height:100px;
18.        text-align:center;
19.    }
20.    li:nth-child(even){
21.        background-color:red;
22.    }
23.    li:nth-child(odd){
24.        background-color:blue;
25.    }   
26.</style>
27.
28.<div class="wrapper">
29.    <ul class="list clear" id="list">
30.        <li>1</li>
31.        <li>2</li>
32.        <li>3</li>
33.        <li>4</li>
34.    </ul>
35.</div>
1.let listBox=document.getElementById("list");
2.
3.listBox.innerHTML+=listBox.innerHTML;
4.
5.utils.css(listBox,"width",utils.css(listBox,"width")*2);
6.
7.setInterVal(function(){
8.    let cur=utils.css(listBox,"left");
9.    cur-=2;
10.    utils.css(listBox,"left",cur);
11.
12.    Math.abs(listBox.offsetLeft)===utils.css(listBox,"width")/2?utils.css(listBox,"left",0)
13.},13)
案例:匀速回到顶部
1.(function (){
2.    let link=document.getElementById("link");
3.    //=>当页面在滚动的时候(鼠标滚轮/键盘按键/手动拖动滚动条/基于JS控制页面滚动的时候...),我们需要随时获取当前页面卷去的高度,如果卷去的高度>一屏幕的高度,让link显示,否则隐藏
4.    //=>window.onscroll:当页面滚动的时候触发的事件
5.    window.onscroll=function(){
6.        let scrollT=document.documentElement.scrollTop,
7.            clientT=document.documentElement.clientTop;
8.        if(scrollT>clientT){
9.            link.style.display="block";
10.        }else{
11.            link.style.displey="none"
12.        }
13.    };
14.    let isRun=false;//=>记录当前是否正在运动,默认false没有运动,当点击A标签开启动画的时候,让其变为true,动画结束让其变为false,在它为true的时候,我们点击A标签不回话再做任何事情,防止重复点击;
15.    //=>如果重复点击,会多次执行函数,也就是会设置多个定时器,走的距离就是所有定时器累加的距离,所以速度回越来越快
16.    link.onclick=function(){
17.        if(isRun){
18.            return;
19.        }
20.        isRun=true;//=>修改的是上级作用域的isRun的值,如果setInterval没有结束,那么isRun的值一直都为true,再点击的时候,函数执行,直接return,下面的都不会执行
21.        let curT=document.documentElement.scrollTop,
22.            time=500,
23.            interval=17,
24.            step=curT/time*interval;
25.
26.        let timer=setInterval(function(){
27.            let curTop=document.documentElement.scrollTop;
28.            if(curTop===0){
29.                isRun=false;
30.                clearInterval(timer);
31.            }
32.            curTop-=step;
33.            document.documentElement.scrollTop=curTop;
34.        }interval);
35.
36.    }
37.})()
JS中的继承
原型继承
让子类的原型指向父类的一个实例
1.function A(){
2.    this.x=100;
3.}
4.A.prototype={
5.    constructor:A,
6.    getX:function(){
7.        console.log(this.x);
8.    }
9.};
10.function B(){
11.    this.y=200;
12.}
13.B.prototype=new A();
14.let f=new B();
 
存在的问题
1.子类可以重写父类原型上的方法,导致父类的实例受到影响
2.父类实例私有的属性以及公有的属性都变为子类实例的共有属性
3.无法使用子类原有原型的上的方法(子类的原型被重定向)
Call继承
把父类在子类中作为普通函数执行,让A中的this变为子类的实例,相当于给子类的实例增加一些父类实例的私有属性和方法
1.function A(){
2.    this.x=100;
3.}
4.A.prototype={
5.    constructor:A,
6.    getX:function(){
7.        console.log(this.x);
8.    }
9.};
10.function B(){//=>this:f
11.    A.call(this);//=>把A执行,让A中的this变为f
12.    this.y=200;
13.}
14.let f=new B();
寄生组合继承
父类实例私有的变为子类实例私有的,父类共有的变为子类共有的 
Object.create() 
Object类上的一个方法 
1.创建一个空对象
2.让新创建的空对象的__proto__指向第一个传递进来的对象
1.let obj={
2.    name:"哈哈"
3.};
4.console.log(Object.create(obj))

1.function A(){
2.    this.x=100;
3.}
4.A.prototype={
5.    constructor:A,
6.    getX:function(){
7.        console.log(this.x);
8.    }
9.};
10.function B(){
11.    A.call(this);
12.    this.y=200;
13.}
14.B.prototype=Object.create(A.prototype);
15.//=>相当于把B的原型指向了新创建的空对象,而空对象的__proto__又指向了A的原型
16.let f=new B();
17.
18.----------
19.为什么不直接用B.prototype=A.prototype?
20.
21.//=>太容易重写原型
22.
23.//B.prototype.xxx=xxx,就会直接修改A的原型,但是使用Object.create的方式,修改B上的原型,B.prototype.xxx=xxx是在给空对象增加或者修改东西,不会影响A原型上的东西

ES6中的类和继承
ES6中创建类
ES6中创建的类是由自己标准语法的,基于这种语法创建出来的类只能NEW执行,不能当做普通函数执行
1.class Fn{//=>这个大括号相当于FN的原型
2.
3.    constructor(n,m)}{//=>constructor相当于Fn,所以这个括号里就是函数体里面的东西
4.        let total=null;
5.        total=n+m;
6.        this.x=n;
7.        this.y=m;
8.        return total;
9.    }
10.
11.    //=>给Fn的原型上设置方法(只能设置方法不能设置属性)
12.    getX(){
13.        console.log(this.x);
14.    }
15.
16.    //=>把Fn当做一个普通对象设置的Fn类上的私有方法(只能设置方法不能设置属性)
17.
18.    static AA(){
19.        console.log(this.y);
20.    }
21.}
22.Fn.aa=100;
23.Fn.prototype.bb=200;
24.let f=new Fn(10,20);

基于ES6创建的类的继承
extends实现了原型继承
super();类似于call继承
1.class A{
2.    constructor(){
3.        this.x=100;
4.    }
5.
6.    getX(){
7.        console.log(this.y);
8.    }
9.}
10.A.aa=100;
11.class B extends A{//=>B的原型
12.    constructor(){
13.        super();
14.        this.y=200;
15.    }
16.
17.    getY(){
18.        console.log(this.x);
19.    }
20.}
21.let f=new B();

B的实例既可以调取B原型上的公有属性和方法,也可以调取A上的公有属性和方法
B的实例也拥有A的实例的私有属性
B类既有自己的私有属性,也可以调取A类的私有属性
练习题
1.class Animal {
2.    constructor(name = 'John Doe', species = '物种'){
3.        this.name = name; this.species = species;
4.    }
5.    sayHello(){
6.        console.log('hello',this.name,this.species)
7.    }
8.}
9.class Sheep extends Animal{
10.    constructor(name = 'Jimmy',species = '羊'){
11.        super(name, species);
12.        //=>相当于Animal.call(this,name,species);
13.    }
14.    sayHello(){
15.        console.log(super.sayHello);
16.        console.log('child'); super.sayHello()//=>super指的是Animal.prototype
17.    }
18.}
19.let sheep = new Sheep('Tom');
20.sheep.sayHello();
21.

定时器
设定一个定时器,并且设定了等待的时间,当到达指定的时间,浏览器会把对应的方法执行
设置定时器会有一个返回值,这个值是一个数字,属于定时器的编号,代表当前是第几个定时器(不管是基于setTimeout还是setInterval创建定时器,这个编号会累加)
setInterval
可执行多次的定时器,也被称为轮循定时器,每间隔interval这么长的时间,都会把设定的方法重新执行一次,直到定时器被清除
语法:setInterval([function],[interval])
[function]:到达时间后执行的方法(设置定时器的时候方法没有执行,到时间浏览器帮我们执行)
[interval]:时间因子(需要等待的时间 MS)
setTimeout
只能执行一次的定时器
语法:setTimeout([function],[interval])
[function]:到达时间后执行的方法(设置定时器的时候方法没有执行,到时间浏览器帮我们执行)
[interval]:时间因子(需要等待的时间 MS)
定时器时间因子即使设置为0也不是立即实行,还是异步编程,每一个浏览器都有一个自己最小的等待和反应时间(谷歌:5~6,IE:10~14)
1.let n=0;
2.setTimeout(()=>{
3.    console.log(++n);
4.},0);
5.console.log(n);
6.
7.//=>结果:0,1
清除定时器
clearTimeout
都可以清除setInterval和setTimeout创建的定时器,本质是根据序号清除浏览器中设定的定时器
clearInterval
都可以清除setInterval和setTimeout创建的定时器,本质是根据序号清除浏览器中设定的定时器
同步编程&&异步编程
JS中大部分都是同步编程
同步编程
任务是按照顺序依次处理,当前这件事没有彻底做完,下一件事是执行不了的
异步编程
定义:当前这件事没有彻底做完,需要等待一段时间才能继续处理,此时我们不等,继续执行下面的任务,当后面的任务完成后,再去把没有彻底完成的事情完成
JS中常用的异步编程
所有的事件绑定都是异步编程
所有的定时器都是异步编程
定时器到达一定时间,也不一定执行
1.let n=0;
2.setInterval(()=>{
3.    console.log(n);
4.},1000)
5.console.log(n);
6.while(1===1){
7.//=>死循环
8.}
AJAX中一般都使用异步编程处理
回调函数也算是异步编程
浏览器规划同步异步的机制
浏览器是多线程的,JS是单线程的(浏览器只给JS执行分配一个线程)
单线程的特点就是一次只能处理一件事情
进程
每个应用程序都可以理解为一个进程,浏览器打开一个页面,就相当于开辟一个进程,在一个进程中,我们需要同时干很多事情,此时我们可以分配多个线程去同时完成多项任务
JS在单线程中实现异步的机制
主要依赖于浏览器的任务队列完成的,浏览器中有两个任务队列,主任务队列用于供JS代码自上而下执行,等待任务队列用于存放异步操作任务
当主任务队列完成后才会到等待任务队列中进行查找,主任务队列完不成,不管等待任务队列中是否有到达时间的,都不处理,继续等待主任务队列完成。
等待任务队列中谁达到条件了(如果又很多都达到条件了,谁先达到的,就先处理谁),就把这个任务重新放到主任务队列中去执行,把这个任务执行完,再去等待中找下一个,以此类推

1.setTimeout(() => {
2.    console.log(1);
3.}, 20);
4.console.log(2);
5.setTimeout(() => {
6.    console.log(3);
7.}, 10);
8.setTimeout(() => {
9.    console.log(4);
10.}, 100);
11.for (let i = 0; i < 90000000; i++) {
12.
13.}
14.console.log(5);

1.setTimeout(() => {
2.    console.log(1);
3.}, 20);
4.console.log(2);
5.setTimeout(() => {
6.    console.log(3);
7.}, 10);
8.console.log(4);
9.for (let i = 0; i < 90000000; i++) {
10.
11.}
12.console.log(5);
13.setTimeout(() => {
14.    console.log(6);
15.}, 8);
16.console.log(7);
17.setTimeout(() => {
18.    console.log(8);
19.}, 15);
20.console.log(9);
21.----
22.循环之前的定时器,在循环还没有结束的时候时间就已经到了,而循环之后的定时器,是要等到循环结束了,才被放到等到任务序列中,也就是与循环之前的相比要完了200MS(循环的时间),所以即使下面的定时器的时间短,也是上面的定时器先到达时间,先被拿到主任务队列内执行
测试程序执行的时间
console.time()、console.timeEnd()
这两个方法配合使用可以用来统计执行某个函数所用的时间
console.time()表示计时开始
console.timeEnd()表示计时结束,
1.console.time('AA');
2.for (var i = 0; i < 90000; i++) {
3.    for (var j = 0; j < 90000; j++) {}
4.}
5.console.timeEnd('AA');

利用new Date()
获取当前客户端本机时间(当前获取的时间不能作为重要的参考依据)
1.let a=new Date();
2.for (var i = 0; i < 90000; i++) {
3.    for (var j = 0; j < 90000; j++) {}
4.}
5.console.log(new Date()-a);

1.var time = new Date();//=>获取当前客户端本机时间(当前获取的时间不能作为重要的参考依据)
2.
3.//=>获取的结果是一个日期格式的对象:Sun Apr 01 2018 15:55:23 GMT+0800 (中国标准时间)
4.
5.typeof new Date() ->'object'
6.
7.//=>time.getFullYear() 获取四位整数年
8.//=>time.getMonth()  获取月(0-11代表1-12月)
9.//=>time.getDate() 获取日
10.//=>time.getDay() 获取星期(0-6代表周日到周六)
11.//=>time.getHours() 获取小时
12.//=>time.getMinutes() 获取分钟
13.//=>time.getSeconds() 获取秒
14.//=>time.getMilliseconds()获取毫秒
15.//=>time.gettime() 获取当前日期距离'1970-1-1 00:00:00'的毫秒差
1.var time = new Date('2017-10-22');//=>当newDate中传递一个时间格式的字符串,相当于把这个字符串转换为标准的时间对象格式(转换完成后,就可以调取上面我们将的那些方法了)
2.
3.//=>时间格式的字符串
4.'2017-10-22'  (IE下识别不了)
5.'2017/10/22'
6.'2017/10/22 16:15:34'
7.1522569836707 (如果传递的是距离1970年的那个毫秒差,也是可以识别转换的,但是这个只能是数字,不能是字符串)
8.
9....
案例:京东倒计时抢购
1.let timeBox=document.querySelector(".timeBox"),
2.    timeSpan=timeBox.querySelector("span"),
3.    _serverTime=null,
4.    autoTimer=null;
5.//=>获取服务器当前的时间,注意缩短客户端接收服务器端的时间
6.let queryTime=function (){
7.    return new Promise((resolve)=>{
8.        let xhr=new XMLHttpRequest();
9.        xhr.open("head","temp.json",true);//=>"head"的方式会比"get"快
10.        xhr.onreadystatechange=function(){
11.            if(xhr.readyState===2 && xhr.status===200){//=>当readystate为2的时候,响应头信息就已经返回了,无需等到4,另外就算请求没有成功也可以得到响应头的信息,但是项目中一般都要加上这句话避免出现404页面
12.                _serverTime=new Date(xhr.getResponseHeader("date"));//=>请求头上得到的时间信息是格林尼治时间,要用new Date转化成北京时间
13.                resolve(_serverTime);
14.            }
15.        }
16.        xhr.send(null);
17.    })
18.}
19.//=>动态计算当前倒计时时间
20.let count=0;//=>记录第几次执行computedTime,因为第一次执行的时候就直接用服务器拿到的时间,第二次之后执行的时候才需要往上累加时间
21.let computedTime=function (){
22.    if(count>0){
23.        let time=_serverTime.getTime();//=>得到距离1970/1/1 00:00:00的时间,再加上1s,再转换回标准北京时间
24.        time+=1000;
25.        _serverTime=new Date(time);
26.    }
27.    count++;
28.    let nowTime=_serverTime,
29.        targetTime=new Date("2018/5/25 23:00:00"),
30.        changeTime=targetTime-nowTime;//=>两个标准时间相减,得到的是毫秒差
31.    if(changeTime<=0){
32.        clearInterval(autoTimer);
33.    }
34.    let hours=Math.floor(changeTime/(1000*60*60));
35.        changeTime=changeTime-hours*60*60*1000;
36.    let minutes=Math.floor(changeTime/(1000*60));
37.        changeTime=changeTime-minutes*60*1000;
38.    let seconds=Math.floor(changeTime/1000);
39.        hours<10?hours="0"+hours:null;
40.        minutes<10?minutes="0"+minutes:null;
41.        seconds<10?seconds="0"+seconds:null;
42.    timeSpan.innerText=` ${hours}:${minutes}:${seconds} `;
43.}
44.queryTime().then(()=>{
45.    computedTime();
46.    autoTimer=setInterval(computedTime,1000);
47.})
48.//=>如果每次都向服务器发送请求获得当前服务器的时间,会造成服务器崩溃,所以在不刷新页面的情况下,我们就向服务器请求一次数据,然后通过定时器手动累加时间,只有当页面重新刷新的时候才会重新向服务器发送请求得到时间
Promise
Promise是ES6中新增的类(new Promise),目的是为了管理JS中的异步编程,也将其称为”Promise设计模式”
Promsie本身是同步的,可以管理异步操作,new Promise会创建一个Promise实例,立即会把当前函数体中的异步操作执行,必须传递一个回调函数进去,不传递会报错
1.new Promise(()=>{
2.    setTimeout(() => {
3.        console.log(3);//=>最后输出3
4.    }, 2000);
5.    console.log(1);//=>先输出1
6.})
7.console.log(2);//=>再输出2
new Promise的回调函数中会传两个参数(resolve,reject),异步操作执行成功,执行resolve方法;异步操作失败了,执行reject方法
Promise原型上有then方法,第一个参数代表是异步执行成功要做的事情(即resolve),第二个参数代表的是异步执行失败要做的事情(即reject)
1.new Promise((resolve,reject)=>{
2.    setTimeout(()=>{
3.        resolve(100);
4.    },1000);
5.}).then((res)=>{
6.    console.log("ok",res);
7.},()=>{
8.    console.log("no");
9.})
then中的对应的方法执行后返回的也是Promise实例(是新的实例,与之前的实例不是一个)可以继续调用then方法,而且第一个then中的回调函数执行的返回结果,会当做实参传给第二个then中对应的回调函数的形参
1.let pro = new Promise((resolve, reject) => {
2.    //=>执行一个异步操作
3.    let xhr = new XMLHttpRequest();
4.    xhr.open('get', 'js/1.js', true);
5.    xhr.onreadystatechange = () => {
6.        if (xhr.readyState === 4 && xhr.status === 200) {
7.            val = xhr.responseText;
8.            resolve(val);
9.        }
10.        if (xhr.status !== 200) {
11.            //=>失败
12.            reject();
13.        }
14.    };
15.    xhr.send(null);
16.});
17.pro.then((res)=>{//=>相当于这个函数就是resolve
18.    console.log(res);//=>输出val,
19.},()=>{//=>相当于这个函数是reject
20.    console.log("no");
21.}).then((res)=>{
22.    console.log(res);//=>100
23.},()=>{})
如果调用多个then,异步操作成功或者失败,先把第一个then中对应的成功或者失败的方法执行,返回一个新的Promise实例,这个新的实例管控第一个then中方法执行的成功还是失败,如果成功了就执行第二个then中成功的方法执行,如果失败了就执行了第二个then中失败的方法,类似于树状图

1.let promise1 = new Promise((resolve, reject) => {
2.    $.ajax({
3.        url: 'json/data2.json',
4.        success(result) {
5.            resolve(result);
6.        },
7.        error(msg) {
8.            reject('no');
9.        }
10.    });
11.});
12.promise1.then(
13.    result => {
14.        console.log('THEN1 OK', result);
15.        return 100;
16.    },
17.    msg => {
18.        console.log('THEN1 NO', msg);
19.        return 100;
20.    }
21.).then(
22.    result => {
23.        console.log('THEN2 OK', result);
24.    },
25.    msg => {
26.        console.log('THEN2 NO', msg);
27.    }
28.);
29.//=>若'json/data2.json'存在,输出'THEN1 OK 获取的数据',再输出'THEN2 OK 100'
30.//=>若'json/data2.json'不存在,输出'THEN1 NO xhr的实例',再输出'THEN2 OK 100'(jQuery的ajax中若请求失败,会把xhr实例作为实参传递给失败时执行的函数)
31.
真实项目中,一般我们不用then同时管理成功和失败的方法,只管理成功的方法,失败的方法由catch方法来管理,catch方法不仅异步请求失败会执行它,第一个then方法执行失败也会执行它,也就是管着兄弟和爹
1.let promise1 = new Promise((resolve, reject) => {
2.    $.ajax({
3.        url: 'json/data2.json',//=>不存在
4.        success(result) {
5.            resolve(result);
6.        },
7.        error(msg) {
8.            reject('no');
9.        }
10.    });
11.});
12.promise1.then(result => {
13.    console.log('THEN1 OK', result);
14.    return 100;
15.}).catch(msg => {
16.    console.log('CATCH1', msg);
17.    return 100;
18.})
19.//=>输出"catch1 no"
1.let promise1 = new Promise((resolve, reject) => {
2.    $.ajax({
3.        url: 'json/data.json',//=>存在
4.        success(result) {
5.            resolve(result);
6.        },
7.        error(msg) {
8.            reject('no');
9.        }
10.    });
11.});
12.promise1.then(result => {
13.    console.log('THEN1 OK', result);
14.    100();
15.    return 100;
16.}).catch(msg => {
17.    console.log('CATCH1', msg);
18.    return 100;
19.})
20."catch1 TypeError: 100 is not a function"
1.let promise1 = new Promise((resolve, reject) => {
2.    $.ajax({
3.        url: 'json/data2.json',//=>不存在
4.        success(result) {
5.            resolve(result);
6.        },
7.        error(msg) {
8.            reject('no');
9.        }
10.    });
11.});
12.promise1.then(result => {
13.    console.log('THEN1 OK', result);
14.    return 100;
15.}).catch(msg => {
16.    console.log('CATCH1', msg);
17.    return 100;
18.}).then(result => {
19.    console.log('THEN2 OK', result);
20.    100();
21.}).catch(msg => {
22.    console.log('CATCH2', msg);
23.});
24.//=>输出'CATCH1 no',
25.//=>'THEN2 OK 100'
26.//=>'CATCH2 TypeError: 100 is not a function'
1.let promise1 = new Promise((resolve, reject) => {
2.    $.ajax({
3.        url: 'json/data2.json',//=>不存在
4.        success(result) {
5.            resolve(result);
6.        },
7.        error(msg) {
8.            reject('no');
9.        }
10.    });
11.});
12.promise1.then(result => {
13.    console.log('THEN1 OK', result);
14.    return 100;
15.}).catch(msg => {
16.    console.log('CATCH1', msg);
17.    100();
18.    return 100;
19.}).then(result => {
20.    console.log('THEN2 OK', result);
21.}).catch(msg => {
22.    console.log('CATCH2', msg);
23.});
24.//=>输出'CATCH1 no'
25.//=>'CATCH2 TypeError: 100 is not a function'
catch方法和then方法中的回调函数执行的过程中如果报错浏览器不会报错,会把报错的内容当做实参传递给下一个then或者catch方法,即使当前的catch和thenreturn值也会被报错覆盖,把报错的内容传递给下一个then或者catch方法,类似于JS中的异常捕获(目的:把抛出异常的错误捕获到,不让其阻断浏览器的继续执行)
1.try{
2.    1();
3.}catch (e){
4.    //=>try中的代码报错了会执行catch
5.    console.log(e.message);
6.} finally{
7.//=>不管try中的代码成功还是失败都会执行
8.    console.log("ok");
9.}
Promise上还有finally方法,不管上面的then或者catch方法执行成功或者失败都会执行finally,但是上面的then或者catch方法的返回值不会传递给finally作为实参,如果上面的then或者catch方法中的回调函数中有报错的话,会直接在浏览器内报错,但是即使这样也不会阻止finally的执行
如果finally方法后面还有then或者catch的话,就当做有没finally方法,管控的还是前面的then和catch
1.let promise1 = new Promise((resolve, reject) => {
2.    $.ajax({
3.        url: 'json/data.json',//=>地址存在
4.        success(result) {
5.            resolve(result);
6.        },
7.        error: reject
8.
9.    });
10.});
11.promise1.then(result => {
12.    console.log('THEN1 OK', result);
13.    100();
14.    return 100;
15.}).catch(msg => {
16.    console.log('CATCH1', msg);
17.    100();
18.    return 100;
19.}).finally(result => {
20.    console.log('THEN2 OK', result);
21.    // 100();
22.}).then(result=>{
23.    console.log("ok",result);
24.}).catch(result=>{
25.    console.log("no",result);
26.});
27.
28.//=>THEN1 OK []
29.//=>CATCH1 TypeError: 100 is not a function
30.//=>THEN2 OK undefined
31.//=>no TypeError: 100 is not a function
使用Promise解决回调地狱
有A,B,C三个请求,实现,A请求成功拿到数据后再执行B请求拿到数据后再执行C请求
1.let promise1=function (){
2.    return new Promise(reslove){
3.        $.ajax({
4.            url:"url1",
5.            method:"get",
6.            cache:false
7.            success:resolve
8.        })
9.    }
10.};
11.let promise2=function (){
12.    return new Promise(reslove){
13.        $.ajax({
14.            url:"url2",
15.            method:"get",
16.            cache:false
17.            success:resolve
18.        })
19.    }
20.};
21.let promise3=function (){
22.    return new Promise(reslove){
23.        $.ajax({
24.            url:"url3",
25.            method:"get",
26.            cache:false
27.            success:resolve
28.        })
29.    }
30.}
31.promise1().then(()=>{
32.    return promise2();
33.}).then(()=>{
34.    promise3();
35.})
回调函数
定义: 把一个函数A当做实参传递给另外一个函数B,在B方法执行的时候,把A执行了,这种机制被称为”回调函数机制”

1、可以给回调函数传递实参执行
2、可以改变回调函数中的this指向
3、可以在宿主函数(它在哪执行的,它的宿主函数就是谁)中接收回调函数执行的返回结果
1.let fn=(callback)=>{
2.    callback&&callback.call(obj,100,200);
3.    let res=callback(10,20);
4.    console.log(res);
5.}
回调函数中的this一般是window,除非宿主函数执行回调函数的时候把this特殊指向了(箭头函数除外:箭头函数中的this是它的上下文)
重写jQuery上的each方法
可以遍历数组,类数组,对象,如果是数组(类数组)和forEach方法类似,如果是对象和for in循环类似
1.let each=function(obj,callback){
2.    if(obj.hasOwnProperty("length")){
3.        for(let i=0;i<obj.length;i++){
4.            let res=callback&&callback.call(obj[i],i,obj[i]);
5.            if(res===false) break;
6.        }else{
7.            for(let key in obj){
8.                if(!obj.hasOwnProperty(key)) break;
9.                let res=callback&&callback.call(obj[key],key,obj[key]);
10.                if(res===false) break;
11.            }
12.        }
13.    }
14.}
重写forEach方法
循环遍历数组中的每一项,每遍历一次把回调函数执行一次,并把当前项和对应的索引传递给回调函数,如果方法中传递第二个参数的话,那么将回调函数中的this指向第二个参数
1.Array.prototype.myforEach=function myforEach(callback,context){
2.    context=context||window;
3.    for(let i=0;i<this.length;i++){
4.        callback.call(context,this[i],i);
5.    }
6.};
重写map方法
循环遍历数组中的每一项,每遍历一次把回调函数执行一次,并把当前项和对应的索引传递给回调函数,并将当前项替换成回调函数的返回值,循环结束后以新数组的形式返回,如果方法中传递第二个参数的话,那么将回调函数中的this指向第二个参数
1.Array.prototype.myMap=function myMap(callback,context){
2.    context=context||window;
3.    let ary=[];
4.    for(let i=0;i<this.length;i++){
5.        let res=callback&&callback.call(context,this[i],i); 
6.        ary.push(res);
7.    }
8.    return ary;
9.}
重写filter方法
循环遍历数组中的每一项,每遍历一次把回调函数执行一次,并把当前项和对应的索引传递给回调函数,将所有回调函数返回值(Boolean后)为true的当前项以新数组的形式返回,如果方法中传递第二个参数的话,那么将回调函数中的this指向第二个参数
1.Array.prototype.myFilter=function myFilter(callback,context){
2.    context=context||window;
3.    let ary=[];
4.    for(let i=0;i<this.length;i++){
5.        let res=callback&&callback.call(context,this[i],i);
6.        if(res){
7.            ary.push(this[i]);
8.        }
9.    }
10.    return ary;
11.}
重写find方法
循环遍历数组中的每一项,每遍历一次把回调函数执行一次,并把当前项和对应的索引传递给回调函数,将所有回调函数返回值(Boolean后)为ture的当前项返回,只要找到一个返回值为true的就不再往下找了
1.Array.prototype.myFind=function myFind(callback){
2.    for(let i=0;i<this.length;i++){
3.        let res=callback&&callback(this[i],i);
4.        if(res){
5.            return this[i];
6.        }
7.    }
8.}
重写replace方法
重写replace方法,第一个参数替换成第二个参数,如果第一个参数是正则,第二个参数是回调函数,正则每匹配一次就把回调函数执行一次,并把exec捕获到的数组中的每一项依次传递给回调函数,回调函数的返回结果会替换被匹配到的内容,原有字符串不变
1.String.prototype.myReplace=function(reg,callback){
2.    let str=this;
3.    if(typeof reg==="string"&&typeof callback==="string"){
4.        if(str.indexOf(reg)<0) return str;
5.        let a=str.indexOf(reg[0]),
6.            b=str.indexOf(reg[reg.length-1]);
7.        str=str.substring(0,a)+callback+str.substring(b+1);
8.    }else if(reg instanceof RegExp&&typeof callback==="function"){
9.        if(!reg.global){
10.            let regExec=reg.exec(this);
11.            let res=callback(...regExec);
12.            let c=regExec[0],
13.                a=str.indexOf(c[0]),
14.                b=str.indexOf(c[c.length-1]);
15.            str=str.substring(0,a)+res+str.substring(b+1);
16.            return str;
17.        }
18.        let regExec=reg.exec(this);
19.        while (regExec){
20.            let res=callback(...regExec);
21.            let c=regExec[0];
22.            let a=str.indexOf(c[0]),
23.                b=str.indexOf(c[c.length-1]);
24.            str=str.substring(0,a)+res+str.substring(b+1);
25.            regExec=reg.exec(this);
26.        }
27.    }else if(typeof reg==="string"&&typeof callback==="function"){
28.        let res=callback();
29.        let a=str.indexOf(reg[0]),
30.            b=str.indexOf(reg[reg.length-1]);
31.        str=str.substring(0,a)+res+str.substring(b+1);
32.    }
33.    return str;
34.};
复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值