閉包
概述:在函數(shù)內(nèi)部返回一個函數(shù),內(nèi)部函數(shù)有外部函數(shù)的引用,這個結(jié)構(gòu)就稱為閉包。
函數(shù)的生命周期
function fn(){
var i = 0
i++
return i
}
console.log(fn())//1 第1個i變量
console.log(fn())//1 第2個i變量
console.log(fn())//1 第3個i變量
函數(shù)預(yù)編譯階段
開辟一個內(nèi)存空間,將對應(yīng)的代碼塊放到這個內(nèi)存空間
函數(shù)的執(zhí)行階段
- 將對應(yīng)的函數(shù)開辟的空間放在執(zhí)行棧上
- 執(zhí)行棧開始執(zhí)行對應(yīng)函數(shù)的對應(yīng)空間的代碼塊
- 這個代碼如果需要開辟空間 它就在這個對應(yīng)的函數(shù)的內(nèi)存空間上開辟
- 當(dāng)你不需要使用這個函數(shù)了,對應(yīng)的函數(shù)的內(nèi)存空間就會被回收,里面的代碼開辟的空間也就被回收了
如果我們需要保持i的狀態(tài),我們可以將這個i放到引用數(shù)據(jù)類型里面。然后保證這個引用數(shù)據(jù)類型對象的引用,這時gc就不會回收對應(yīng)的這個i了
function fn(){
var i = 0
i++
retrun {
i
}
}
var obj = fn()
console.log(obj.i) //1
綜上所述,我們可以保持對應(yīng)i的引用,保證i不會被回收,以返回一個引用數(shù)據(jù)類型來保持對應(yīng)i的引用。 那么對應(yīng)的函數(shù)也是一個引用數(shù)據(jù)類型,那么我們是不是也可以通過返回函數(shù)的形式來做到保證i的唯一性。
function fn(){
var i = 0
return function(){
i++
console.log(i)
}
}
var f = fn()
f() //1
f() //2
f() //3
而上面這種保證i不被回收的機(jī)制就叫做閉包。(返回一個引用數(shù)據(jù)類型 這個里面保證對應(yīng)的對應(yīng)i的引用 從而不被回收)
閉包的優(yōu)劣
優(yōu)勢
- 內(nèi)部函數(shù)擁有外部函數(shù)參數(shù)和變量的引用,使用我們的參數(shù)和變量的作用范圍被擴(kuò)大。
- 對應(yīng)的參數(shù)不被回收,在使用的時候就不需要重新開辟空間,速度更快。
- 作為緩存
劣勢
- 內(nèi)部函數(shù)要一直保持對外部函數(shù)里面參數(shù)和變量的引用。
- 因?yàn)椴粫换厥漳敲磳?yīng)的內(nèi)存空間就會一直占用。
閉包的應(yīng)用
防抖
在規(guī)定時間內(nèi)只執(zhí)行一次 執(zhí)行最后一次
例:(等電梯)

//第一個參數(shù)是做的操作 第二個參數(shù)是等待時間
function debounce(fn,delay){
var timer = null
return function(){
clearTimeout(timer) //清除上一次的等待
//開始新的等待
timer = setTimeout(fn,delay)
}
}
節(jié)流
在規(guī)定時間內(nèi)執(zhí)行第一次 減少執(zhí)行次數(shù)
例:(紅路燈)

//操作 執(zhí)行一次的時長
function throttle(fn,delay){
var timer = null
return function(){
//判斷上一次是否走完
if(timer) return
//上一次走完了開始下一次
timer = setTimeout(()=>{
fn()
//走完了要將節(jié)流閥設(shè)置為false
timer = null
},delay)
}
}
防抖和節(jié)流的區(qū)別
- 防抖執(zhí)行最后一次 節(jié)流執(zhí)行第一次
- 防抖在規(guī)定時間內(nèi)只執(zhí)行一次 節(jié)流是在規(guī)定時間內(nèi)減少對應(yīng)的執(zhí)行次數(shù)
- 防抖對應(yīng)的開始下一次先要清除上一次 節(jié)流開始下一次先要判斷上一次是否執(zhí)行完畢
函數(shù)柯里化
將多個參數(shù)的函數(shù)拆分為多個單參數(shù)的函數(shù) 可以自由的組合
例:
function sum(a,b){
return a+b
}
sum(1,2)
簡單的函數(shù)柯里化
function sum(a){
return function(b){
return function(c){
return a+b+c
}
}
}
console.log(`sum(1)(2)(3)`, sum(1)(2)(3));//6
console.log(`sum(1)(2)`, sum(1)(2));//fn
核心:參數(shù)沒有夠返回對應(yīng)的函數(shù) 參數(shù)夠了返回結(jié)果
高階函數(shù)柯里化
//傳遞一個函數(shù) (參數(shù)沒到返回函數(shù) 參數(shù)到了返回結(jié)果)
function currying(fn) {
//獲取currying傳遞的參數(shù)
let args = Array.prototype.slice.call(arguments,1)
return function () {
//將對應(yīng)的函數(shù)的參數(shù)和curry傳遞參數(shù)做連接
let arg = Array.from(arguments).concat(args)
//判斷參數(shù)個數(shù)是否一樣
if(arg.length < fn.length){
//參數(shù)沒到返回函數(shù)
return currying.call(this,fn,...arg)
}else{
//參數(shù)到了 調(diào)用方法返回結(jié)果
return fn.apply(this,arg)
}
}
}
調(diào)用
function sum(a,b,c){
return a+b+c
}
console.log(`sum(1,2,3)`, sum(1,2,3));
let fn = currying(sum)
console.log(`fn(2)`, fn(2));//函數(shù)
console.log(`fn(2)(3)`, fn(2)(3));//函數(shù)
console.log(`fn(2)(3)(1)`, fn(2)(3)(1));//6
console.log(` fn()()()(2)()()(3)()()()(1)`, fn()()()(2)()()(3)()()()(10));//15
Promise
概述:promise是es6新增的一個類,這個類翻譯為承諾,它有三種狀態(tài) 等待狀態(tài),成功狀態(tài),拒絕狀態(tài)。它被設(shè)計(jì)為異步的,它里面的內(nèi)容是異步的(方法為異步的)

promise的三種狀態(tài)
- 等待狀態(tài)(沒有處理) pending
- 成功狀態(tài) (有對應(yīng)的處理) fulfilled (里面resolve方法調(diào)用)
- 失敗狀態(tài) (有對應(yīng)的處理)rejected (里面代碼報(bào)錯 或者 調(diào)用reject)
promise對象構(gòu)建
new Promise((成功的函數(shù),失敗的函數(shù))=>{
代碼塊
})
//里面?zhèn)鬟f的參數(shù)是一個函數(shù)
//這個傳遞的函數(shù)里面有倆個參數(shù) 這個倆個參數(shù)也是一個函數(shù)
//這個函數(shù)里面的第一個參數(shù)為成功的函數(shù) resolve 第二個參數(shù)為失敗的函數(shù) reject (這個倆個函數(shù)都是異步的)
var promise = new Promise((resolve,reject)=>{
//包含異步的代碼
console.log('hello promise')
})
promise的方法
原型方法
then 執(zhí)行成功的回調(diào)
var promise = new Promise((resolve,reject)=>{
//成功的函數(shù)調(diào)用 傳遞對應(yīng)的參數(shù)
resolve('成功')
})
promise.then((res)=>{
console.log(`第一次then`, res);
return 'hello'
}).then((res)=>{
console.log(`第二次then`, res);
return 'world'
}).then((res)=>{
console.log(`第三次then`, res);
return 'abc'
}).then() //值穿透 當(dāng)你的then沒有處理它會給到下一個處理
.then((res)=>{
console.log(`第n次then`, res);
})
catch 執(zhí)行失敗
var promise = new Promise((resolve,reject)=>{
reject('失敗')
// throw new Error('失敗了')
})
//.catch默認(rèn)請求下只執(zhí)行第一個 如果需要走下面的 那么需要你報(bào)錯
promise.catch((error)=>{
console.log(`第一次`, error);
throw new Error('失敗了')
}).catch((error)=>{
console.log(`第二次`, error);
throw new Error('失敗了')
}).catch((error)=>{
console.log(`第三次`, error);
throw new Error('失敗了')
}).catch()
.catch((error)=>{
console.log(`第n次`, error);
})
finally 執(zhí)行完成調(diào)用的
//promise promise只能滿足于一種狀態(tài) 進(jìn)入到成功它就成功了 進(jìn)入失敗就失敗了
var success = new Promise((resolve,reject)=>{
//成功的函數(shù)調(diào)用 傳遞對應(yīng)的參數(shù)
resolve('成功')
reject('失敗')
// throw new Error('失敗了')
})
//成功的回調(diào) 倆個參數(shù) 成功的函數(shù) 失敗的函數(shù)
success.then((res)=>{//res會接收resolve傳遞的參數(shù)
console.log(`res`, res);
},(error)=>{//error 接收reject傳遞的參數(shù)
console.log(`error`, error);
})
//失敗的回調(diào) 參數(shù)1個 傳遞為一個函數(shù) 這個函數(shù)可以接收rejected傳遞的參數(shù)
success.catch((error)=>{
console.log(`rejected`, error);
})
//完成就能調(diào)用的函數(shù) (成功 失?。?success.finally(()=>{
console.log(`完成了`);
})
靜態(tài)方法
- resolve (返回成功狀態(tài)的promise)
- reject (返回失敗狀態(tài)的promise)
- all (并行執(zhí)行所有的promise 如果遇到rejected就返回reject的promise 如果全部成功就返回所有的結(jié)果(promiseresult))
- allSettled (互不影響執(zhí)行對應(yīng)的promise 返回所有的結(jié)果(狀態(tài)一定是成功))
- race (返回最快執(zhí)行完成的promise)
//靜態(tài)方法 Promise.方法名
//resolve方法
//返回一個成功的promise對象
var promise = Promise.resolve('hello')
console.log(`promise`, promise);
promise.then(res=>{
console.log(res);
})
// reject返回一個失敗的promise對象
var promise1 = Promise.reject('錯誤')
console.log(`promise1`, promise1);
promise1.catch(error=>{
console.log(error);
})
//all 傳入一個promise數(shù)組 并行執(zhí)行promise數(shù)組里面的promise (如果有一個是rejected 那么整體都是rejected)
var promise2 = Promise.resolve('hello')
var promise3 = Promise.reject('錯誤')
// var promise3 = Promise.resolve('成功')
var promise4 = Promise.resolve('world')
var promise5 = Promise.resolve('你好')
//all方法返回的是一個promise對象 如果全部成功對應(yīng)的promiseResult里面的結(jié)果就是所有成功 否則就是錯誤的結(jié)果
var promise6 = Promise.all([promise2,promise3,promise4,promise5])
console.log(promise6);
//傳入一個promise數(shù)組 返回一個promise對象 不會互相影響 返回所有結(jié)果(狀態(tài)為成功)
var promise7 = Promise.allSettled([promise2,promise3,promise4,promise5])
console.log(promise7);
//競速 race 傳入promise數(shù)組 返回最快走完的promise
var promise8 = Promise.race([promise2,promise3,promise4,promise5])
console.log(promise8);
promise的三種狀態(tài)圖

回調(diào)地獄
概述:回調(diào)函數(shù)的無限嵌套導(dǎo)致當(dāng)前代碼失去了對應(yīng)的維護(hù)價值及對應(yīng)的可讀性。
例:
//傳入一個函數(shù)作為回調(diào)函數(shù) 在對應(yīng)代碼執(zhí)行完成調(diào)用
function fn(fn) {
setTimeout(function () {
console.log('10');
//走完了以后回調(diào)函數(shù)執(zhí)行
fn()
}, 1000)
}
fn(() => {
console.log(1);
})
//多個回調(diào)函數(shù)嵌套 回調(diào)地獄 (回調(diào)函數(shù)的無限嵌套 代碼的可讀性 可維護(hù)性 已經(jīng)失去了)
fn(() => {
console.log(1);
fn(() => {
console.log(2);
fn(() => {
console.log(3);
fn(() => {
console.log(4);
fn(() => {
console.log(5);
fn(() => {
console.log(6);
....
})
})
})
})
})
})
promise來解決回調(diào)地獄(鏈?zhǔn)秸{(diào)用)
在.then里面返回一個新的promise對象 在對應(yīng)的異步代碼執(zhí)行完后調(diào)用resolve
//利用promise來解決回調(diào)地獄的問題
new Promise((resolve, reject) => {
setTimeout(() => {
console.log(1);
resolve()
});
}).then(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(2);
resolve()
});
})
}).then(()=>{
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(3);
resolve()
});
})
}).then(()=>{
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(4);
resolve()
});
})
}).then(()=>{
console.log(5);
})

async await (es7新增的)
概述:async和await是對應(yīng)的倆個連用的關(guān)鍵詞,async是修飾函數(shù)的,await是修飾promise的。await只能在async內(nèi)使用。async修飾的函數(shù)返回一個promise對象,await修飾的promise對象會占用當(dāng)前的線程 直到對應(yīng)的promise執(zhí)行完成才會釋放。
async function fn() {
await new Promise((resolve,reject)=>{ //如果沒有放行后面的不會執(zhí)行
setTimeout(() => {
console.log('hello');
resolve()
})
})
console.log('world');
}
//async修飾完函數(shù)執(zhí)行會返回一個promise對象
console.log(fn());
//async修飾的函數(shù)返回的promise對象
//里面的報(bào)錯 會使當(dāng)前的promise對象的狀態(tài)為rejected
//如果里面return內(nèi)容那么內(nèi)容將會傳遞給對應(yīng)的then
async function fn1(){
throw new Error('錯誤')
// return '我是fn1'
}
fn1().then(res=>{
console.log(res);
},error=>{
console.log(error);
})
//await會使用當(dāng)前的函數(shù)的線程占用 直到對應(yīng)的修飾的promise執(zhí)行完成
// await Promise.reject() 報(bào)錯
利用async和await來解決回調(diào)地獄
function fn(v,delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(v);
resolve()
},delay);
})
}
async function fn1(){
await fn(1,1000)
await fn(2,2000)
await fn(3,100)
await fn(4,0)
console.log(5);
}
fn1()
例:

總結(jié)
- async修飾函數(shù)的
- await修飾promise對象
- async里面使用await 那么如果這個await修飾的promise沒有執(zhí)行完,那么對應(yīng)的async修飾的函數(shù)返回promise狀態(tài)時pending。
- 如果async修飾的函數(shù)內(nèi)什么都沒有那么對應(yīng)返回的promise狀態(tài)是成功(默認(rèn)函數(shù)返回undefined)
- async修飾的函數(shù) 返回值就是成功 返回的值傳遞給then方法
- async修飾的函數(shù)如果里面報(bào)錯 那么返回的是失敗 傳遞的值為報(bào)的錯
- await只能在async里面使用 await會使當(dāng)前的函數(shù)陷入等待
代碼執(zhí)行機(jī)制
同步代碼執(zhí)行比異步代碼快
同步代碼的執(zhí)行是利用對應(yīng)的js引擎解析的
異步代碼執(zhí)行是利用事件輪詢機(jī)制執(zhí)行的
事件輪詢機(jī)制

- 先找script標(biāo)簽里面的微任務(wù)
- 按照微任務(wù)隊(duì)列執(zhí)行完對應(yīng)的微任務(wù)
- 進(jìn)入下一個宏任務(wù) 執(zhí)行對應(yīng)的宏任務(wù)代碼
- 進(jìn)行宏任務(wù)對應(yīng)微任務(wù)隊(duì)列 執(zhí)行對應(yīng)微任務(wù)
- 再進(jìn)行到下一個宏任務(wù) 執(zhí)行對應(yīng)的微任務(wù)
- 直到對應(yīng)的宏任務(wù)隊(duì)列和微任務(wù)隊(duì)列被清空
宏任務(wù)
script 定時器(setInterval setTimeout) 事件...
微任務(wù)
promise.then promise.catch nextTick ....