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;
};
複製代碼
相關文章
相關標籤/搜索