day20 閉包和promise
閉包
概述:
閉包就是在函數(shù)內(nèi)部返回一個函數(shù),內(nèi)部函數(shù)有外部函數(shù)的引用。這個結構就稱為閉包。
函數(shù)的生命周期
函數(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ù)在預編譯階段
開辟一個內(nèi)存空間
將對應的代碼塊放到這個內(nèi)存空間
函數(shù)的執(zhí)行階段
將對應的函數(shù)開辟的這個空間放在執(zhí)行棧上
執(zhí)行棧就開始執(zhí)行對應的函數(shù)對應的空間的代碼塊
這個代碼如果需要開辟空間 它就在這個對應的這個函數(shù)的內(nèi)存空間上開辟
當你不需要使用這個函數(shù)了 對應的函數(shù)的內(nèi)存空間就會被回收 那么里面的代碼開辟的空間也就被
回收了
如果我們需要保持i的狀態(tài) 那么我們可以將這個i放到這個引用數(shù)據(jù)類型里面 然后保證這個引用數(shù)據(jù)類型對象的引用,這個時候gc就不會回收對應的這個i了
function fn(){
var i = 0
i++
retrun {
i
}
}
var obj = fn()
console.log(obj.i) //1
通過上述代碼 我們可以保持對應i的引用,保證i不會被回收,以返回一個引用數(shù)據(jù)類型來保持對應
i的引用。 那么對應的函數(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不被回收的機制就叫做閉包。(返回一個引用數(shù)據(jù)類型 這個里面保證對應的對應i的引用從而不被回收)
閉包的優(yōu)劣
優(yōu)勢
內(nèi)部函數(shù)擁有外部函數(shù)參數(shù)和變量的引用,使用我們的參數(shù)和變量的作用范圍被擴大。
對應的參數(shù)不被回收,在使用的時候就不需要重新開辟空間,速度更快。
作為緩存
劣勢
內(nèi)部函數(shù)要一直保持對外部函數(shù)里面參數(shù)和變量的引用。
因為不會被回收那么對應的內(nèi)存空間就會一直占用。
閉包的應用
防抖(在規(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é)流閥設置為false
timer = null
},delay)
}
}
防抖和節(jié)流的區(qū)別
防抖執(zhí)行最后一次 節(jié)流執(zhí)行第一次
防抖在規(guī)定時間內(nèi)只執(zhí)行一次 節(jié)流是在規(guī)定時間內(nèi)減少對應的執(zhí)行次數(shù)
防抖對應的開始下一次先要清除上一次 節(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ù)沒有夠返回對應的函數(shù) 參數(shù)夠了返回結果
高階函數(shù)柯里化
//傳遞一個函數(shù) (參數(shù)沒到返回函數(shù) 參數(shù)到了返回結果)
function currying(fn) {
//獲取currying傳遞的參數(shù)
let args = Array.prototype.slice.call(arguments,1)
return function () {
//將對應的函數(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ù)到了 調用方法返回結果
return fn.apply(this,arg)
}
}
}
調用
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)。它被設計為異步的,它里面的內(nèi)容是異步的(方法為異步的)

promise的三種狀態(tài)
等待狀態(tài)(沒有處理) pending
成功狀態(tài) (有對應的處理) fulfilled (里面resolve方法調用)
失敗狀態(tài) (有對應的處理)rejected (里面代碼報錯 或者 調用reject)
構建promise對象
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í)行成功的回調
var promise = new Promise((resolve,reject)=>{
//成功的函數(shù)調用 傳遞對應的參數(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() //值穿透 當你的then沒有處理它會給到下一個處理
.
then((res)=>{
console.log(`第n次then`, res);
})
catch 執(zhí)行失敗
var promise = new Promise((resolve,reject)=>{
reject('失敗')
// throw new Error('失敗了')
})
//.catch默認請求下只執(zhí)行第一個 如果需要走下面的 那么需要你報錯
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í)行完成調用的
示例
//promise promise只能滿足于一種狀態(tài) 進入到成功它就成功了 進入失敗就失敗了
var success = new Promise((resolve,reject)=>{
//成功的函數(shù)調用 傳遞對應的參數(shù)
resolve('成功')
reject('失敗')
// throw new Error('失敗了')
})
//成功的回調 倆個參數(shù) 成功的函數(shù) 失敗的函數(shù)
success.then((res)=>{//res會接收resolve傳遞的參數(shù)
console.log(`res`, res);
},(error)=>{//error 接收reject傳遞的參數(shù)
console.log(`error`, error);
})
//失敗的回調 參數(shù)1個 傳遞為一個函數(shù) 這個函數(shù)可以接收rejected傳遞的參數(shù)
success.catch((error)=>{
console.log(`rejected`, error);
})
//完成就能調用的函數(shù) (成功 失?。?/p>
success.finally(()=>{
console.log(`完成了`); })
靜態(tài)方法
resolve (返回成功狀態(tài)的promise)
reject (返回失敗狀態(tài)的promise)
all (并行執(zhí)行所有的promise 如果遇到rejected就返回reject的promise 如果全部成功就返回所有
的結果(promiseresult))
allSettled (互不影響執(zhí)行對應的promise 返回所有的結果(狀態(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對象 如果全部成功對應的promiseResult里面的結果就是所有成功 否則
就是錯誤的結果
var promise6 = Promise.all([promise2,promise3,promise4,promise5])
console.log(promise6);
//傳入一個promise數(shù)組 返回一個promise對象 不會互相影響 返回所有結果(狀態(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)圖
回調地獄
概述:回調函數(shù)的無限嵌套導致當前代碼失去了對應的維護價值及對應的可讀性。
示例
//傳入一個函數(shù)作為回調函數(shù) 在對應代碼執(zhí)行完成調用
function fn(fn) {
setTimeout(function () {
console.log('10');
//走完了以后回調函數(shù)執(zhí)行
fn()
}, 1000)
}
fn(() => {
console.log(1);
})
//多個回調函數(shù)嵌套 回調地獄 (回調函數(shù)的無限嵌套 代碼的可讀性 可維護性 已經(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來解決回調地獄(鏈式調用)
在.then里面返回一個新的promise對象 在對應的異步代碼執(zhí)行完后調用resolve
//利用promise來解決回調地獄的問題
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是對應的倆個連用的關鍵詞,async是修飾函數(shù)的,await是修飾promise的。
await只能在async內(nèi)使用。async修飾的函數(shù)返回一個promise對象,await修飾的promise對象會
占用當前的線程 直到對應的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對象
//里面的報錯 會使當前的promise對象的狀態(tài)為rejected
//如果里面return內(nèi)容那么內(nèi)容將會傳遞給對應的then
async function fn1(){
throw new Error('錯誤')
// return '我是fn1'
}
fn1().then(res=>{
console.log(res);
},error=>{
console.log(error);
})
//await會使用當前的函數(shù)的線程占用 直到對應的修飾的promise執(zhí)行完成
// await Promise.reject() 報錯
利用async和await來解決回調地獄
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()
圖例

總結
async修飾函數(shù)的
await修飾promise對象
async里面使用await 那么如果這個await修飾的promise沒有執(zhí)行完,那么對應的async修飾的函
數(shù)返回promise狀態(tài)時pending。
如果async修飾的函數(shù)內(nèi)什么都沒有那么對應返回的promise狀態(tài)是成功(默認函數(shù)返回
undefined)
async修飾的函數(shù) 返回值就是成功 返回的值傳遞給then方法
async修飾的函數(shù)如果里面報錯 那么返回的是失敗 傳遞的值為報的錯
await只能在async里面使用 await會使當前的函數(shù)陷入等待
代碼執(zhí)行機制
同步代碼執(zhí)行比異步代碼快
同步代碼的執(zhí)行是利用對應的js引擎解析的
異步代碼執(zhí)行是利用事件輪詢機制執(zhí)行的
事件輪詢機制
先找script標簽里面的微任務
按照微任務隊列執(zhí)行完對應的微任務
進入下一個宏任務 執(zhí)行對應的宏任務代碼
進行宏任務對應微任務隊列 執(zhí)行對應微任務
再進行到下一個宏任務 執(zhí)行對應的微任務
直到對應的宏任務隊列和微任務隊列被清空
宏任務
script 定時器(setInterval setTimeout) 事件...
微任務
promise.then promise.catch nextTick ....