day20-闭包和promise

本文详细介绍了JavaScript中的闭包,包括其定义、作用域、内存管理以及优缺点。同时,文章讨论了函数的预编译过程、执行上下文和执行栈的概念。此外,还深入探讨了防止全局污染、内存泄漏和优化技术,如防抖和节流。最后,文章涵盖了Promise的使用和事件循环机制在异步编程中的重要性。
摘要由CSDN通过智能技术生成

闭包概述

函数嵌套函数,内部函数拥有外部函数的引用,对应外部函数的变量或参数不会被gc回收。

作用域

  • 全局作用域 (global):里面的 this 指向对应的 global 对象,也就是说对应的全局作用域的变量其实都是 global 对象的属性
  • 局部作用域 (函数作用域)

函数的预编译过程

局部作用域

1. 创建AO对象 (Activation Object 执行上下文) ,开辟的内存空间 (会在堆中开辟一个跟函数名
一样名字的空间)
2. 给函数里面的形参、变量进行赋值操作 (undefined)
3. 形参和实参同步
4. 找到函数声明 赋值给函数体

function fn(a=1,b=2){
    console.log(a,b,c) //1,2,undefined
    var c = function(){
        console.log(1)
    }
    console.log(c) //函数
}
fn()

全局作用域(全局暴露 对变量来说有全局污染的问题)

1. 创建GO对象 (Global Object 全局对象)(给对应的global对象添加属性)
2. 给函数里面的形参、变量进行赋值操作 (undefined)
3. 形参和实参同步
4. 找到函数声明 赋值给函数体

console.log(a) //function
var a = 10
function a(){}
console.log(a) //10 (全局污染)

函数执行过程

  • 去堆中找到对应的函数名所对应的地址,放入执行栈中执行(压栈)
  • 执行过程中(开辟对应的执行空间)使用的对应执行上下文进行执行(函数体)
  • 返回对应的结果(结果不会销毁)
  • 执行完(销毁对应的执行空间 以及对应的函数体) (出栈)
    function fn() {
            let b = 22
            var a = 20
            a++
            // console.log(a, b);
            return a
        }
        fn() 
//函数在执行中,会在堆中查找并将其推入栈中执行,执行完毕后会进行一个销毁操作
        console.log(fn());
        console.log(fn());
function fn(){
    var i = 0
    return function(){
        i++ //标记为yes i也不会回收
        console.log(i)
    }
}
//闭包占用内存大 容易造成内存泄漏
var closureFn = fn() //接收一个函数
closureFn()//1
closureFn()//2
closureFn()//3
//解决内存泄漏
fn = null

从上述代码可以得到对应函数中return 一个引用数据类型 在这个引用数据类型内容保持对应的外部的一个参数或者是变量的引用那么这个变量将不会被回收。(扩大了函数内容变量的作用范围)

闭包的优缺点

优点

  • 防止变量的全局污染 (这个变量是局部变量)
  • 扩大了变量的作用域
  • 缓存对应的数据

缺点

  • 过度使用闭包容易造成内存泄漏 (当前这个空间已经没用了 但是空间没有被销毁)
  • 对应内存的耗费大(性能差) (内部函数时刻保持对应的外部函数变量或者参数的引用)

闭包的用途

  • 作为缓存 (容量小 速度快)
  • 防抖
  • 节流
  • 函数柯里化

防抖(debounce)

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

// 在n秒内点击后,又在n秒内触发,n会重新计数,所以只会打印最后一次n秒执行完毕后的内容
        function debounce(callback, delay) {
            var timer = null
            return function() {
                // 清除上一次的延时器
                clearTimeout(timer)
                    // 设置延时器
                timer = setTimeout(() => {
                    callback()
                        // clearTimeout(timer)
                }, delay);
            }
        }
        var btn = document.querySelector('button')
        var fn = debounce(function() {
            console.log('点击了');
        }, 1000)
        btn.onclick = fn

节流 (throttling)

在规定时间内执行第一次,执行完继续执行。(减少对应的次数)

 // 在n秒内点击后,n秒内再点击不会触发,n秒内只执行一次
        function throttling(callback, delay) {
            var flag = false
            return function() {
                clearTimeout(timer)
                if (!flag) {
                    var timer = setTimeout(() => {
                        callback()
                        console.log(delay);
                        flag = false
                    }, delay);
                }
                flag = true
            }
        }
        var btn = document.querySelector('button')
        var fn = throttling(function() {
            console.log('点击了');
        }, 3000)
        btn.onclick = fn

函数柯里化

概述:将一个多参数的函数 分成多个函数(彼此之间可以任意组成)

核心:参数个数没到对应的个数返回的是函数 参数个数到了返回的是值

普通函数、函数柯里化、高阶函数柯里化 对比如下:

    //普通函数
        function sum(a, b, c, d) {
            return a + b + c + d
        }
        console.log(sum(1, 5, 3, 2)); //11

        // 函数柯里化
        function carry(a) {
            return function(b) {
                return function(c) {
                    return a + b + c
                }
            }
        }
        console.log(carry(1)(1)(1)); //3
        console.log(carry(5, 3, 2)); // f(b)
        console.log(carry(2)(5)); //f(c)
        console.log(carry()()()); //NaN

        // 高阶函数柯里化
        function carrying(fn) {
            let args = Array.prototype.slice.call(arguments, 1)
            return function() {
                let allargs = args.concat([...arguments])
                if (allargs.length < fn.length) {
                    return carrying.call(this, fn, ...allargs)
                } else {
                    return fn.apply(this, allargs)
                }
            }
        }
        let fn = carrying(sum)
        console.log(fn(1, 2, 3, 6)); //12
        console.log(fn(5, 4)(1)(5)); //15
        console.log(fn(5, 3, 6)(8)); //22

Promise

promise 是 ES6 新增的一个类,翻译为承诺,它是用于解决异步问题的(替代回调函数)。

promise的三种状态

  • 等待 pedding
  • 成功 fulfiiled(有对应的处理)
  • 失败 rejected(有对应的处理)

promise的构建

//传入函数来进行构建 传入的函数内有俩个参数传递 (resolve、reject都是函数)
//new Promise是同步执行的代码 它的里面可以包含异步代码
new Promise((resolve,reject)=>{
    console.log('hello') //也是同步的
})

promise的方法及属性

属性 (内置属性,只读无法访问)

  • PromiseState 表示当前状态
  • PromiseResult 表示当前的结果

方法(原型方法和静态方法)

原型方法:then、catch、finally

then :处理对应的一个promise状态变化的

  • then有俩个参数函数 一个是成功的处理函数,一个是拒绝的错误函数
  • 成功的处理函数里面可以接收对应的 resolve 函数调用携带的参数,相当于接收了return 的数据return 也会调用的对应的 then
  • 拒绝的处理函数里面可以接收对应的 reject 函数调用携带的参数
  • 拒绝的处理函数还可以捕获的对应的 promise 中抛出的错误
  • 在 then 中 return 的数据会被下一级的 then 方法接收
  • then 里面会发生值穿透 (上层的 then 方法没有对应的函数做为参数)
let promise = new Promise((resolve,reject)=>{
    console.log('hello')
    //promise的状态是唯一的
    // resolve(123) //成功状态
    // return 123 //状态没有发生变化 不会调用then
    // reject(456) //拒绝状态
    throw new Error('你好错了')
    //then方法 它里面传入俩个参数 (都是函数)
    //第一个参数为成功的处理函数(携带 resolve函数传递的数据 相当于接收了return的数据return也会调用的对应的then)
    //第二个参数为拒绝的处理函数(携带 reject函数传递的参数 或者 捕获抛出的错误)
    }).then((result)=>{
        console.log(result)
    },(err)=>{
        console.log(err)
})
//then的传值 使用return
new Promise((resolve)=>{
    resolve()
}).then(()=>{
    return 123
}).then((result)=>{
    console.log(result)//123
})
//then的值穿透 发生在上层的then方法里面没有函数为参数
new Promise((resolve)=>{
    resolve('hello')
})
    // .then(()=>{}) 只有有参数不会发生值传统
    .then()
    .then()
    .then((result)=>{
        console.log(result)//123
})

catch :捕获 promise 错误以及获取对应的 promise 的 reject 方法调用的结果(跟 then 方法中的第二个参数一模一样)

new Promise((resolve,reject)=>{
// reject(11)
    throw new Error('我错了')
}).catch((err)=>{ //接收reject函数调用的参数 或者捕获对应的错误
    console.log(err)
})
//catch也会发生值穿透
new Promise((resolve,reject)=>{
// reject(11)
    throw new Error('我错了')
}).catch()
    .catch()
    .catch((err)=>{ //接收reject函数调用的参数 或者捕获对应的错误
        console.log(err)
})

finally :状态发生变化就会调用的函数

//finally 不管成功还是失败都会调用的函数(状态变化)
new Promise((resolve,reject)=>{
    resolve(111)
    // reject(11)
    // throw new Error('我错了')
}).finally(()=>{
    console.log('完成了')
})

静态方法:resolve、reject、race、all、allsettled

resolve :产生一个状态为成功的 promise 对象

//返回成功状态的promise resolve
let promise1 = Promise.resolve(456)
console.log(promise1)
promise1.then((result)=>{
    console.log('成功了'+result)
})

reject :产生一个状态为拒绝的 promise 对象

//reject方法 产生一个拒绝状态的promise
let promise2 = Promise.reject(123)
console.log(promise2)
promise2.catch((err)=>{
    console.log(err)
})

race :竞速 (传入一个 promise 数组 返回执行完成最快的 promise)

//竞速方法race 比较执行速度 谁先执行完(不区分成功失败)返回执行快的promise
let promise3 = Promise.reject(1)
let promise4 = Promise.reject(2)
// let promise3 = Promise.resolve(1)
let newPromise = Promise.race([promise3,promise4]) //成功状态比拒绝状态要快
console.log(newPromise)

all :并行执行传入 promise 数组 (遇到了rejected 那么就获取到 rejected 的 promise,如果全部成功会接收所有的 promise 结果)

//all 同步并行执行多个promise 返回一个promise ( 全部成功 包含所有的结果 状态为成功)
// (如果遇到了拒绝那么返回的就是拒绝状态的 只会接收到拒绝传递的值)
let promise5 = Promise.reject(5)
let promise6 = Promise.reject(6)
let promise7 = Promise.reject(7)
// let promise7 = Promise.resolve(7)
let promise8 = Promise.reject(8)
let promiseAll = Promise.all([promise5,promise6,promise7,promise8])
console.log(promiseAll)

allsettled :并行执行传入promise数组 (只要执行完成那么返回都是对应的成功状态,且会接收所有的promise结果

//allSettled 并行执行多个promise 返回所有的结果 (只要执行完成那么状态就是成功) 新增的
let promiseAllsettled =
Promise.allSettled([promise5,promise6,promise7,promise8])
console.log(promiseAllsettled)

回调地狱 

问题提出:异步代码如何同步执行,使用回调函数,但回调函数嵌套过深会导致回调地狱问题

//回调函数解决的问题
function fn(message, delay = 0, callback) {
    setTimeout(() => {
        console.log(message)
        callback()
    }, delay)
}
//这个代码没有任何问题,但是这个代码的可维护性低,可读性低(回调地狱)
//这个代码没有可用性(避免回调函数不能嵌套过深)
fn('1', 1000, () => {
    fn('2', 500, () => {
        fn('3', 1000, () => {
            fn('4', 2000, () => {
                fn('5', 100, () => {
                    fn('6', 1000, () => {
                        // ....
                    })
                })
            })
         })
    })
})

使用 promise 来解决对应的回调地狱 

核心:在对应的 then 方法返回一个新的 promise 再调用下一个 then

//resolve 触发成功的函数 对应的处理的方法 then
//reject 触发失败的函数 对应的处理的方法 catch
new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log(1);
        //调用成功的函数
        resolve()
    }, 1000)
}).then(() => {
//在then方法里面通过返回对应的新的promise来调用下一个then
    return new Promise((resolve,reject) => {
        setTimeout(() => {
            console.log(2);
            resolve()
        }, 500)
    })
}).then(()=>{
    return new Promise((resolve,reject) => {
        setTimeout(() => {
            console.log(3);
            resolve()
        }, 500)
    })
}).then(()=>{
    return new Promise((resolve,reject) => {
        setTimeout(() => {
            console.log(4);
            resolve()
        }, 200)
    })
})

promise状态变更

async及awiat

  • async 表示异步、是用于修饰对应的函数的,被 async 修饰的函数,执行会返回一个 promise 对象。
  • awiat 是用于修饰 promise 的,它只能在 async 修饰的函数内使用,它会让当前的 ( js 引擎线程在当前的执行上下文中等待 ) 等待,当前修饰 promise 的状态不为 pending 的时候就会释放。
  • awiat 和 async 属于语法糖 (awiat 一定要在 async 里面使用 且 awiat 修饰的是 promise)

【注意事项】

  • 修饰的函数内容返回值相当于调用了 resolve 方法,返回的值会被传递给 then
  • 在修饰函数内报错相当于调用了 reject 方法,错误会被传递给 catch
//async用于修饰对应的函数的异步
async function sayHello(){
    console.log('123')
    //因为它返回的值,在async修饰的函数内容,返回内容相当于调用了resolve方法,更改状态为成功
    //报错相当于调用了reject
    // throw new Error() 相当于reject调用
    //函数默认返回undefined
    // return 123 相当于resolve调用
}
//async修饰的函数调用会返回一个promise对象
let promise = sayHello()
console.log(promise)
promise.then((result)=>{
    console.log(result)
})
async function say(){
//使当前js引擎线程等待 等待对应的状态更改才释放
    await new Promise((resolve,reject)=>{
        setTimeout(()=>{
            console.log(1);
            resolve()
        },2000)
    })
    console.log(2)
}
say() // 1 2

使用async和awiat解决回调地狱

async function test(){
//使当前js引擎线程等待 等待对应的状态更改才释放
    await new Promise((resolve,reject)=>{
        setTimeout(()=>{
            console.log(1);
            resolve()
        },2000)
    })
    await new Promise((resolve,reject)=>{
        setTimeout(()=>{
            console.log(2);
            resolve()
        },1000)
    })
    await new Promise((resolve,reject)=>{
        setTimeout(()=>{
            console.log(3);
            resolve()
        },500)
    })
    console.log(4)
}
test()

事件轮询(EventLoop)

概述:

eventLoop事件轮询,针对于对应的异步任务的执行相关操作(定时器、事件、请求....)。

事件队列

队列是先进先出一个数据结构,事件队列就是其异步任务的一个队列(先进去先执行 后进行后执行)

执行栈

栈是一个先进后出的一个数据结构,它主要用执行对应的js代码。

事件队列的分类

宏任务 (大的任务 线程直接分配的)

  • script 标签
  • 定时器(setInterval,setTimeout)
  • I/O ...

微任务 (小的任务)

  • promise.then、 promise.catch、 promise.finally
  • process.nextTick:当没有dom时是微任务,有dom的时候就是宏任务

宏任务进入宏任务队列 微任务进入微任务队列
eventLoop主要是控制宏任务队列及微任务队列的任务执行,也就是等待和发送消息或事件的程序结构

  • 先执行同步任务后执行异步任务
  • 先宏后微
  • 一个个的宏任务走进再走其包含的微任务

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值