闭包与回调地狱的解决方案

闭包

概述:

闭包就是在函数内部返回一个函数,内部函数有外部函数的引用。这个结构就称为闭包。

函数的生命周期

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变量

函数在预编译阶段

  • 开辟一个内存空间

  • 将对应的代码块放到这个内存空间

函数的执行阶段

  • 将对应的函数开辟的这个空间放在执行栈上

  • 执行栈就开始执行对应的函数对应的空间的代码块

  • 这个代码如果需要开辟空间 它就在这个对应的这个函数的内存空间上开辟

  • 当你不需要使用这个函数了 对应的函数的内存空间就会被回收 那么里面的代码开辟的空间也就被回收了

如果我们需要保持i的状态 那么我们可以将这个i放到这个引用数据类型里面 然后保证这个引用数据类型对象的引用,这个时候gc就不会回收对应的这个i了

function fn(){
    var i = 0
    i++
    retrun {
        i
    }
}
var obj = fn()
console.log(obj.i) //1
  • 通过上述代码 我们可以保持对应i的引用,保证i不会被回收,以返回一个引用数据类型来保持对应i的引用。 那么对应的函数也是一个引用数据类型,那么我们是不是也可以通过返回函数的形式来做到保证i的唯一性。

function fn(){
  var i = 0
  return function(){
    i++
    console.log(i)
  }
}
var f = fn()
f() //1
f() //2 
f() //3

而上面这种保证i不被回收的机制就叫做闭包。(返回一个引用数据类型 这个里面保证对应的对应i的引用 从而不被回收)

闭包的优劣

优势

  • 内部函数拥有外部函数参数和变量的引用,使用我们的参数和变量的作用范围被扩大。

  • 对应的参数不被回收,在使用的时候就不需要重新开辟空间,速度更快。

  • 作为缓存

劣势

  • 内部函数要一直保持对外部函数里面参数和变量的引用。

  • 因为不会被回收那么对应的内存空间就会一直占用。

闭包的应用

防抖(在规定时间内只执行一次 执行最后一次)

示例(等电梯)

//第一个参数是做的操作 第二个参数是等待时间
function debounce(fn,delay){
    var timer = null
    return function(){
        clearTimeout(timer) //清除上一次的等待
        //开始新的等待
        timer = setTimeout(fn,delay)
    }
}

节流 (在规定时间内执行第一次 减少执行次数)

示例(红绿灯)

//操作  执行一次的时长
function throttle(fn,delay){
    var timer = null
    return function(){
        //判断上一次是否走完
        if(timer) return
        //上一次走完了开始下一次
        timer = setTimeout(()=>{
            fn()
            //走完了要将节流阀设置为false
            timer = null    
        },delay)
    }
}

防抖和节流的区别

  • 防抖执行最后一次 节流执行第一次

  • 防抖在规定时间内只执行一次 节流是在规定时间内减少对应的执行次数

  • 防抖对应的开始下一次先要清除上一次 节流开始下一次先要判断上一次是否执行完毕

函数柯里化 (将多个参数的函数拆分为多个单参数的函数 可以自由的组合)

示例

function sum(a,b){
 return a+b
}
sum(1,2)

简单的函数柯里化

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

核心就是参数没有够返回对应的函数 参数够了返回结果

高阶函数柯里化

//传递一个函数 (参数没到返回函数 参数到了返回结果)
function currying(fn) {
    //获取currying传递的参数
    let args = Array.prototype.slice.call(arguments,1)
    return function () {
        //将对应的函数的参数和curry传递参数做连接
        let arg = Array.from(arguments).concat(args)
        //判断参数个数是否一样
        if(arg.length < fn.length){
            //参数没到返回函数
            return currying.call(this,fn,...arg)
        }else{
            //参数到了 调用方法返回结果
            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));//函数
console.log(`fn(2)(3)`, fn(2)(3));//函数
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新增的一个类,这个类翻译为承诺,它有三种状态 等待状态,成功状态,拒绝状态。它被设计为异步的,它里面的内容是异步的(方法为异步的)

promise的三种状态

  • 等待状态(没有处理) pending

  • 成功状态 (有对应的处理) fulfilled (里面resolve方法调用)

  • 失败状态 (有对应的处理)rejected (里面代码报错 或者 调用reject)

构建promise对象

new Promise((成功的函数,失败的函数)=>{
    代码块
})
//里面传递的参数是一个函数
//这个传递的函数里面有俩个参数 这个俩个参数也是一个函数
//这个函数里面的第一个参数为成功的函数 resolve  第二个参数为失败的函数 reject (这个俩个函数都是异步的)
var promise = new Promise((resolve,reject)=>{
    //包含异步的代码
    console.log('hello promise')
})

promise的方法

原型方法

  • then 执行成功的回调

 
  • finally 执行完成调用的

示例

 

静态方法

  • resolve (返回成功状态的promise)

  • reject (返回失败状态的promise)

  • all (并行执行所有的promise 如果遇到rejected就返回reject的promise 如果全部成功就返回所有的结果(promiseresult))

  • allSettled (互不影响执行对应的promise 返回所有的结果(状态一定是成功))

  • race (返回最快执行完成的promise)

 

promise的三种状态图

回调地狱

概述:回调函数的无限嵌套导致当前代码失去了对应的维护价值及对应的可读性。

示例

 

promise来解决回调地狱(链式调用)

在.then里面返回一个新的promise对象 在对应的异步代码执行完后调用resolve

//利用promise来解决回调地狱的问题

async await (es7新增的)

概述:async和await是对应的俩个连用的关键词,async是修饰函数的await是修饰promise的await只能在async内使用async修饰的函数返回一个promise对象await修饰的promise对象会占用当前的线程 直到对应的promise执行完成才会释放。

async function fn() {
    await new Promise((resolve,reject)=>{ //如果没有放行后面的不会执行
        setTimeout(() => {
            console.log('hello');
            resolve()
        })
    })
    console.log('world');
}
//async修饰完函数执行会返回一个promise对象
console.log(fn());
//async修饰的函数返回的promise对象 
//里面的报错 会使当前的promise对象的状态为rejected 
//如果里面return内容那么内容将会传递给对应的then
async function fn1(){
    throw new Error('错误')
    // return '我是fn1'
}
fn1().then(res=>{
    console.log(res);
},error=>{
    console.log(error);
})
//await会使用当前的函数的线程占用 直到对应的修饰的promise执行完成
// 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修饰函数的

  • await修饰promise对象

  • async里面使用await 那么如果这个await修饰的promise没有执行完,那么对应的async修饰的函数返回promise状态时pending。

  • 如果async修饰的函数内什么都没有那么对应返回的promise状态是成功(默认函数返回undefined)

  • async修饰的函数 返回值就是成功 返回的值传递给then方法

  • async修饰的函数如果里面报错 那么返回的是失败 传递的值为报的错

  • await只能在async里面使用 await会使当前的函数陷入等待

代码执行机制

同步代码执行比异步代码快

同步代码的执行是利用对应的js引擎解析的

异步代码执行是利用事件轮询机制执行的

事件轮询机制

  • 先找script标签里面的微任务

  • 按照微任务队列执行完对应的微任务

  • 进入下一个宏任务 执行对应的宏任务代码

  • 进行宏任务对应微任务队列 执行对应微任务

  • 再进行到下一个宏任务 执行对应的微任务

  • 直到对应的宏任务队列和微任务队列被清空

宏任务

script 定时器(setInterval setTimeout) 事件...

微任务

promise.then promise.catch nextTick ....

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值