閉包、promise和回調(diào)地獄

閉包

概述:在函數(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)

  1. 等待狀態(tài)(沒有處理) pending
  2. 成功狀態(tài) (有對應(yīng)的處理) fulfilled (里面resolve方法調(diào)用)
  3. 失敗狀態(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 ....

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、閉包 概述:閉包就是在函數(shù)內(nèi)部返回一個函數(shù),內(nèi)部函數(shù)有外部函數(shù)的引用,這個結(jié)構(gòu)稱為閉包。 函數(shù)的生命周期: 1...
    詆毀_c424閱讀 739評論 0 0
  • day20 閉包和promise 閉包 概述: 閉包就是在函數(shù)內(nèi)部返回一個函數(shù),內(nèi)部函數(shù)有外部函數(shù)的引用。這個結(jié)構(gòu)...
    瘋油精閱讀 281評論 0 0
  • 閉包 1、概述 閉包是一種書寫代碼,這種結(jié)構(gòu)擁有的特性就是內(nèi)部的空間在使用中不會被回收。(內(nèi)部的變量以及對應(yīng)的參數(shù)...
    yukinoys閱讀 515評論 0 0
  • 閉包 閉包指的是那些引用了另一個函數(shù)作用域中變量的函數(shù),通常是在嵌套函數(shù)中實(shí)現(xiàn)的。 概述:閉包是一種書寫代碼一種結(jié)...
    蒜泥搗莓閱讀 666評論 0 1
  • 回調(diào)地獄demo promise的基本使用 構(gòu)造器必須要定一個參數(shù),如果不給就是報(bào)錯.例如,new promise...
    淚滴在琴上閱讀 2,041評論 0 6

友情鏈接更多精彩內(nèi)容