JavaScript(二)-cnblog

JavaScript(二)-cnblog

异步编程

1. Generator函数

  • 生成迭代器函数,与普通函数不同,可以分段执行

基本使用

function *go(){
    console.log('1')
    // yield "a" 这个整体 说next函数的一次返回值
    
    // 下一次next的参数传递给a,与右边无关
    let a=yield "a"
    
    console.log("2")
    
    
    let b=yield a
    
    console.log("3")
    return b
}
// 直接执行返回的是一个迭代器对象
let it=go("test")

// 第一次next,返回值是一个对象
let r1=it.next() // {done:false,value:"a"}

// 第二次next
let r2=it.next("传递的参数") // 参数会赋值给上一次的yield的左边
// 也即 let a="传递的参数"

// 返回值 yield a //也就是a {done:false,value:"传递的参数"}


// 第三次next,走完了
let r3=it.next("666") // "666参数给上一次yield的左边 let b="666" 

// r3={done:true,value:"666"}

一道例题

        function* test(num) {
            let x = 3 * (yield(num + 1));
            let y = yield(x / 3);
            return (x + y + num);
        }
        let n = test(6);
        console.log(n.next()); // {done:false,value:7}
        console.log(n.next(3)); // {done:false,value:3}
        console.log(n.next(3)); // {done:false,value:18}

for of 迭代Generator函数

        function* test() {
            yield 1;
            yield 2;
            yield 3;
            yield 4;
            yield 5;
            return 6;
        }
        for (let v of test()) {
            console.log(v);
        }

// 1
// 2
//3
// 4
// 5
// 6

对象可迭代

  • 本来 for of 不能用于对象的属性的遍历
let obj={
    name:'zs',
    age:18
}

function* test(obj){
    // 我们yield化
    let keys=Reflect.ownKeys(obj)
    for(let key of keys){
        // yield 右边的值,即为迭代的值
        yield [key,obj[key]]
    }
}

for(let item of test(obj)){
    console.log(item)
}

// {0:"name",1:"zs"}
// {0:"age",1:18}

2. yield * 语句

  • 用于在Genarator函数中调用另一个Genarator函数
   function* test() {
            yield 'a';
            yield 'b';
        }

        function* test1() {
            yield 'x';
            yield* test();
            yield 'y';
        }
        for (let v of test1()) {
            console.log(v);
        }


// 等价于
        function* test() {
            yield 'a';
            yield 'b';
        }

        function* test1() {
            yield 'x';
            for (let v of test()) {
                console.log(v);
            }
            yield 'y';
        }
        for (let v of test1()) {
            console.log(v);
        }

3. Genarator函数中的this指向

  • 创建构造函数
function* Person(){
    yield this.name=""
    yield this.age=""
}

let p=new Person() // 报错

// 解决
let p={}

// 修改this指向
let it=Person.bind(p)()

it.next()

4. Generator 的应用

状态管理
  • 需求:实现状态的循环 0,1,2…循环
// 实现图片的切换

let btn=document.querySelector("#btn")
let img=document.querySelector("#img")

// 传统写法
let flag=false
btn.addEventListener("click",function(){
    if(!flag){
        img.src='images/b.png'
    }else{
        img.src='images/a.png'
    }
    
    flag=!flag
    
})
  • 使用yield每次返回不同状态
let it=test()

btn.addEventListener("click",function(){
    
    it.next()	// 每走一步指向一次循环内的操作

})

function *test(){
    	while(true){
            img.src='images/b.png'
            yield 1
            img.src='images/a.png'
            yield 2
        }
}
实现异步请求

// 发起ajax请求
function ajax(url){
    makeAjaxCall(url,function(res){
    it.next(response)
})
}


// 我们写一个Generator 函数,把这个ajax请求独立成一步,然后和同步代码同步指向

function* f(){
    // 同步等待ajaxi
    let result=yield ajax(url)
    console.log(result)
}

let it=f()

it.next()

5. Promise对象

基本使用

let promise=new Promise(function(resolve,reject){
    
    setTimeout(function(){
        let num=Math.random()
        
        if(num<0.3){
            resolve("成功")
        }else{
            reject("失败")
        }
    },3000)
})

promise.then(
    function(value){
        console.log(value)
    },
    
    function(reason){
        console.log(reason)
    }
)

函数的promise化,promise化ajax请求


// 返回值为一个promise对象
let myAjax=function(url){
    
    let p=new Promise((resolve,reject)=>{
        
        // 正常发起ajax请求
        let xhr=new XMLHttpRequest()
        xhr.open(url)
        xhr.setRequestHeader("Accept","application/json")
        xhr.send()
        xhr.onreadystatechange=function(){
            if(xhr.readystate===4&&xhr.status===200){
                resolve(xhr.responseText)
            }else{
                reject(new Error(xhr.statusText))
            }
        }
    })
    
    return p
}

myAjax("http://xxx.com").then(res=>{
    // 成功的回调
},err=>{
    // 失败的回调
})

promise化链式调用解决回调地狱问题

  • .then方法返回一个全新的promise对象

let p1=myAjax("http://x.com").then(res=>{
    // 成功之后发起下一次ajax请求
    
    return myAjax(res)
},err=>{
    
})

p1.then(res=>{
    // 第二次ajax请求的解果
    console.log(res)
},err=>{
    
})

// 而不是回调地狱的格式

myAjax("http://x.com").then(res=>{
    // 成功之后发起下一次ajax请求
    
    myAjax(res).then(res2=>{
        // 二次ajax请求的解构
    },err2=>{
        
    })
},err=>{
    
})

Promise.all

  • 并行的异步请求
// 其中一个失败,Promise.all失败
Promise.all([
    myAjax("http://x.com"),
    myAjax("http://p.com"),
    // 注意如果这个请求失败,但是后面捕获到了(添加了catch),返回的是一个全新的promise对象,不会认为请求失败
    myAjax("http://g.com").catch(err=>)
]).then(res=>{
    
},err=>{
    
}).catch(err=>{
    
})

Promise.race

  • 多个异步操作,一个成功就结束promise
// 应用场景:1s内请求没有回来,就认为超时了
Promise.all([
    myAjax("http://x.com"),
    myAjax("http://p.com"),
    //时间超时
    setTimeout(()=>reject(new Error('timeout')1000)
]).then(res=>{
    
},err=>{
    
}).catch(err=>{
    
})
    
    

promise.resolve(val)

  • 将值转换成功的promise对象

promise.reject(val)

  • 将值转换为失败的promise对象

6. 手写Promise


// 构建自己的promise对象

function MyPromise(task){
    let that=this
    
    // 设置当前promise的对象
    that.status='Pending'
    
    // 设置resolve或者reject的值
    that.value=undefined
    
    // 设置成功或者失败的回调函数数组
    this.onResolvedCallbacks=[]
    this.onRejectedCallbacks=[]
    
    // 定义resolve和reject函数
    function resolve(value){
         // reject函数被执行
        // 判断状态,状态只能改变一次,每次返回新的promise对象
        if(that.status==='Pending'){
            that.status='Resolved'
            // 修改完成之后,(.then)执行成功或者失败的处理函数
            that.value=value
            // 执行成功的回调函数
            this.onResolvedCallbacks.forEach(item=>{
                item(that.value)
            })
        }
    }
    
    function reject(reason){
        // reject函数被执行
        // 判断状态,状态只能改变一次,每次返回新的promise对象
        if(that.status==='Pending'){
            that.status='Rejected'
            // 修改完成之后,(.then)执行成功或者失败的处理函数
            that.value=value
            // 执行失败的回调函数
            this.onRejectedCallbacks.forEach(item=>{
                item(that.value)
            })
        }
    }
    
    // 执行resolve和reject函数
    // 我们处理.catch的方法
    try{
        task(resolve,reject)
    }catch(e){
        reject(e)
    }
    
}

// 每个实例对象上都存在.then方法
MyPromise.prototype.then=function(onFulfilled,onRejected){
    let that=this
    if(that.status==='resolved'){
        onFulFilled(that.value)
    }
    
    if(that.status==='rejected'){
        onRejected(this.value)
    }
    // 每次.then是不是都会传入成功失败的回调函数
    that.onResolvedCallbacks.push(onFulfilled)
    that.onRejectedCallbacks.push(onRejected)
}


// 使用promise对象
// 传递的参数为一个函数,参数为resolve和reject对象
let p=new MyPromise(function(resolve,reject){
    // 在这个函数体里面会调用resolve和reject函数
})
  • 总结:看起来更像是一种设计模式,对一个异步操作,有成功和失败的状态,成功怎么处理,失败怎么处理 (if,else ===> getType() 0:fnxx 1:fnxxx)

7. 异步编程总结

  • 回调函数:回调地狱问题
  • promise:处理回调地狱问题(.then链式调用)
  • Generator:异步操作在代码里体现为同步
  • async:promise+Generator的合成体

async

        async function test() {
            let result = await Math.random();
            console.log(result);         
        }
        test();
  • Generator
function* main() {
  let result = yield request("http://xxx.com/api");
  let resp = JSON.parse(result);
    console.log(resp.value);
}

function request(url) {
  makeAjaxCall(url, function(response){
    it.next(response);
  });
}

let it = main();
it.next();

注意到

let result = await Math.random();

let result = yield request("http://xxx.com/api");


// 是不是很像

8. async 的基本使用

function ajax(second){
    return new Promise(function(resolve,reject){
        
        setTimeout(()=>{
            let num=Math.random()
            
            if(num>0.5){
                resolve('成功了')
            }else{
                reject('失败了')
            }
        },second)
    })
}

async function awaitDemo(){
    let result=await sleep(3000)
    return result
}


awaitDemo().then((res)=>{
    
},err=>{
    
})

存在依赖关系的async

function ajax(second){
    return new Promise(function(resolve,reject){
        
        setTimeout(()=>{
            let num=Math.random()
            
            if(num>0.5){
                resolve('成功了')
            }else{
                reject('失败了')
            }
        },second)
    })
}

async function awaitDemo(){
    // 这一步的result作为另一个异步操作的输入
    let result=await sleep(3000)
    // 这里我们可以先对result进行处理
    // ... 一系列同步操作
    // 其实await修饰的的右方是异步操作
    // 但执行还是按同步的规则来,也就是result的额外处理完成后,下次请求才会发起
    let result2=await sleep(3000+result)
    let result3=await sleep(result2+3000)
    return result
}


awaitDemo().then((res)=>{
    
},err=>{
    
})

并行关系的异步请求

function ajax(second){
    return new Promise(function(resolve,reject){
        
        setTimeout(()=>{
            let num=Math.random()
            
            if(num>0.5){
                resolve('成功了')
            }else{
                reject('失败了')
            }
        },second)
    })
}

async function awaitDemo(){
    
    // 三个ajax请求相互没有关联,并行发送
    // 全部完成后关闭loading效果
    let p1=ajax(3000)
    let p2=ajax(3000)
    let p3=ajax(3000)
    
    let ps=await Promise.all([p1,p2,p3])
    
    // ps为三次请求结果的数组
    console.log('关闭loading')
}


awaitDemo().then((res)=>{
    
},err=>{
    
})

模块化

1. 模块化的发展过程

  • 最主要的问题:解决模块之间的冲突问题

每个模块导出一个对象,方法和属性都挂载在对象上

var moduleObj = {
    userName: 'zhangsan',
    fn: function () {
        console.log('hello world')
    }
}

立即执行函数,形成一个独立的函数作用域

; (function () {
    var userName = 'lisi';
    function fn1() {
        console.log(userName);
    }
    function fn2() {
        console.log('hello world')
    }
    window.moduleObj = {
        fn1: fn1,
        fn2: fn2
    };
})()

2. 模块化规范

node端

  • CommonJS
1. 一个文件一个模块
2. 导出module.exports
3. 导入required
4. 每个模块独立作用域

AMD:异步模块化定义规范

// 解决浏览器端使用异步加载模块效率低的问题

ES

浏览器端的导入导出规范
1. 严格模式,默认thisundefined
2. 每个模块独立作用域
3. 延迟脚本执行,defer

// 基本使用
// src属性可用也可不要,可以直接使用import语法
<script type="module" src>
	// import 语法    
    
</script>
  • 导出规则
export {v1,v2}  // 导出的是v1,和v2遍历

export default {v1,v2} // 默认导出,导出的是一个对象,包含v1,v2属性

1、注意语法的问题
2、通过export 对成员进行导出操作,导出的是成员的引用
3、导出的成员是只读的,那么我们导入了以后是不能对成员进行修改的。
  • 导入规则
1import后面的from跟的是导入的文件的路径,并且是一个完整路径。
2、如果我们这里是执行某个模块,并不需要提取其中的成员
import {} from './module.js'
import './module.js
3、如果某个模块中导出的成员比较多,同时我们都需要这些导出的模块成员,
import * as m from './module.js'
4、如果需要进行动态的导入
import('./module.js').then(function(module) {
        console.log(module.)
   })
 5、在进行导出的时候,使用了exportexport default,导入import应该怎样进行处理?
 import title, { userName, userAge } from "./module.js";

项目的组件导入技巧

  • components文件夹

button.js

export const button='button'

text.js

const text='编辑组件'
export default text

index.js

export {button} from './button.js'
export {default as text} from './text.js'

其他页面使用

import {button,text} from './components/index.js'

性能优化

1. 内存管理

  • 内存的申请,使用,释放

2. 垃圾回收

  • 释放不在被引用的对象占用的内存空间

3. 垃圾回收算法

  • 引用计数法
// 1. 标记对象存在的引用树
// 2. 引用数量为0释放

// 优点,立即回收,减少查询暂停的情况

// 缺点,循环引用时无法释放,时间开销大
  • 标记清除法

标记:标记从顶层对象找不到的变量

清除:清除被标记的变量

缺点

  • 磁盘碎片化,内存空间不连续

标记整理算法

  • 标记:标记从顶层对象找不到的变量
  • 整理:把标记和没有标记的都放在一块连续的空间,然后释放标记的

优点

  • 解决了磁盘碎片化的问题

缺点

  • 时间开销变大

4. v8引擎的分代回收

新生代回收算法

1. 回收时,分成from和to空间(空白且与from空间等大)
2. 标记from空间中活动的空间
3. 移动标记的空间到to空间
4. 释放from空间
5. 交换from to空间

老生代回收算法

// 标记清除
1. 标记活动的空间
2. 释放没有标记的空间

// 标记整理
1. 标记活动的空间
2. 移动标记的空间到连续的内存块
3. 释放前面一段没有标记的空间

6、V8引擎垃圾回收的机制

V8引擎采用分代回收的思想,将内存分为新生代和老生代。

第一步: 在From空间中分配了3个对象A、B、C

第二步:GC进来判断对象B没有其他引用,可以回收,对象A和C依然为活跃对象.

第三步:将活跃对象A、C从From空间复制到To空间

第四步:清空From空间的全部内存

第五步:交换From空间和To空间

第六步:在From空间中又新增了2个对象D、E

第七步:下一轮GC进来发现对象D没有引用了,做标记

第八步:将活跃对象A、C、E从From空间复制到To空间

第九步:清空From空间全部内存

第十步:继续交换From空间和To空间,开始下一轮

当一个对象经过多次复制后仍然存活,它就会被认为是生命周期较长的对象,这种生命周期较长的对象会被移动到老生代中。

对象从新生代移动到老生代的过程就叫做晋升。

对象晋升的条件主要有两个:

第一:对象从From空间复制到To空间时,会检查它的内存地址来判断这个对象是否已经经历过一次清除回收。如果已经经历过了,会将该对象从From空间移动到老生代空间中,如果没有,则复制到To空间

第二:当要从From空间复制一个对象到To空间时,如果To空间已经使用了超过25%,则这个对象直接晋升到老生代中。设置25%这个阈值的原因是当这次清除回收完成后,这个To空间会变为From空间,接下来的内存分配将在这个空间中进行。如果占比过高,内存就不够了,这样就会影响后续的内存分配。

下面我们再来看一下**V8引擎对老生代对象回收的实现过程。**

垃圾回收12

垃圾回收13

垃圾回收14

垃圾回收15

垃圾回收16

垃圾回收17

7. 全局变量问题

  • 局部作用域查找全局作用域的变量耗时
  • 内存占用时间长
    做标记

[外链图片转存中…(img-gxnDlueQ-1678503311904)]

第八步:将活跃对象A、C、E从From空间复制到To空间

[外链图片转存中…(img-yf4umkzQ-1678503311904)]

第九步:清空From空间全部内存

[外链图片转存中…(img-814FBnh2-1678503311904)]

第十步:继续交换From空间和To空间,开始下一轮

[外链图片转存中…(img-jgCOgTdY-1678503311905)]

当一个对象经过多次复制后仍然存活,它就会被认为是生命周期较长的对象,这种生命周期较长的对象会被移动到老生代中。

对象从新生代移动到老生代的过程就叫做晋升。

对象晋升的条件主要有两个:

第一:对象从From空间复制到To空间时,会检查它的内存地址来判断这个对象是否已经经历过一次清除回收。如果已经经历过了,会将该对象从From空间移动到老生代空间中,如果没有,则复制到To空间

第二:当要从From空间复制一个对象到To空间时,如果To空间已经使用了超过25%,则这个对象直接晋升到老生代中。设置25%这个阈值的原因是当这次清除回收完成后,这个To空间会变为From空间,接下来的内存分配将在这个空间中进行。如果占比过高,内存就不够了,这样就会影响后续的内存分配。

下面我们再来看一下**V8引擎对老生代对象回收的实现过程。**

[外链图片转存中…(img-1FRHfKYP-1678503311905)]

[外链图片转存中…(img-dLR0OSv0-1678503311905)]

[外链图片转存中…(img-PNmMLCft-1678503311906)]

[外链图片转存中…(img-Oi38cME3-1678503311906)]

[外链图片转存中…(img-GTdZKFRm-1678503311906)]

[外链图片转存中…(img-SjVE1Pgf-1678503311907)]

[外链图片转存中…(img-dGjSL31d-1678503311907)]

7. 全局变量问题

  • 局部作用域查找全局作用域的变量耗时
  • 内存占用时间长
  • 变量命名冲突
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值