JavaScript回炉重生(二)

140 阅读5分钟

异步

回调函数在我们有异步请求时,经常被用到。但是由于回调地狱,不得不让我们去探寻更优雅的书写方式

Promise

promise有两个关键点,一个是状态status一个是值value

起初,当我们new 一个 Promise 对象时候,promise的状态是pending(挂起状态)

当执行到resolve和reject函数的情况下,才会触发状态的改变。

当然,一旦状态改变,我们就会发生值得传递

function test () {
        return new Promise((resolve, reject) => {
                setTimeout( () => {
                    resolve('hahah, this is promise')   //状态更改为fullfilled
                }, 3000)
            })
        }
 test().then((res) => {
	console.log('this is res', res)
    return new Promise((resolve, reject) => {
      setTimeout( () => {
        resolve('this is the second')
       }, 2000)
    })
   }).then (
     (res) => {
        console.log(res)
       }
  )

一旦resolve触发,status更改后,对应的值会传到.then中的函数中。

这时我们要注意then的参数有两个,一个是成功时调用的函数,一个是不成功时调用的函数。

但我们不为then传递正确的函数参数时,.then会返回一个空的promise

Promise.all

当我们异步并行接收多个请求时

const one = Promise...
const two = Promise...
const three = Promise...
Promise.all([one,two,three]).then()

Promise.race

竞态获得请求,看哪个请求回来的快

总结

总的看来promise到底比过去的回调函数好在了哪。首先,我们将一个个的异步操作都封装到一个promise对象中,这样我们再操作这些异步函数特别方便,就好比变量一样。

当一个个异步函数像变量以后,我们就可以顺序的处理请求,同时还可以并行或者竞态的处理请求,这是过去回调函数做做不到的。

最后,promise就好像给回调函数套了一层开关,而回调函数更改promise的状态

async...await

async...await语法糖对promise用法做出了改进

async function test () {
    return 12
}
test.then((val)=>{console.log(item)})

通过关键字async可以让异步函数将返回值自动转化为promise对象

return 12 == return Promise.resolve(12)

当我们在异步函数中使用异步函数,会出现函数调用顺序问题。这时我们应该使用await关键字

反射

传统函数调用方式是根据对象操作函数,但是反射则是根据函数判断执行哪个对象

let price = 92.3
console.log(Reflect.apply(price>100?Math.floor:Math.ceil,null,[price])

可以看出反射机制实际上是一种动态的操作,我们事先知道要发生的动作,但是却不知道这个动作将要施加在哪个对象上,这一切都是一个动态变化的

ES5中许多object的操作都可以通过reflect实现,但是reflect的有object实现不了的功能,reflect更加规范化,而且也是为了的发展方向。归于reflect的api我会慢慢更新

Reflect.construct

for await of

目标是实现异步遍历

代理

可以理解为是对数据的一种映像或是一种投影,我们可以通过这个投影读写数据,但是并不会对原数据造成影响

let obj ={
    price: 134,
    name:'zhangsan'
}
let b = new Proxy(obj,{
    get (target,key) {
        if (target[key]>130){
            return 666
        }
        else {
            return target[key]
        }
    },
     set (target,key,value) {
         target[key]=value
    }
})
console.log(b.price,b.name) //666 "zhangsan"
在proxy第二个参数中,我们为读添加的限制

由定义方式可见,这是一种数据结构,创建proxy对象我们需要向构造函数传入两个参数。分别为代理对象和代理操作

//对于复杂的操作函数,我们可以进行解耦
let validator = (target,key,value)=>{
    target[key]=value
}
set: validator

可撤销代理

let b = Proxy.revocable(...)
console.log(b.proxy.price)
b.revoke()  //撤销这个代理

生成器

生成器功能类似数组,它最大的特点是可控,也就是可以手动控制每一步的向下执行

function * test () {
    for(let i = 0; i<10 ; i++ ) {
        yield alert(i)
    }
}
let one = test()
one.next()
one.next()
one.next()

每当我们next,函数就会自动向下走一步,也就是寻找下一个yield。但是注意yield的返回是undefined的

function * test () {
    let one = yield [1,2,3]
    console.log(one)
}
let b = test()
b.next(10)
b.next(20) //20

当next中带有参数时,会有赋值作用

迭代器

迭代器的功能十分强大,我们可以通过自定义函数的方式,对复杂的数据结构进行操作,以达到实现复杂结构可以遍历。

迭代器有2个原则:一个是要写symbol.iterator一个是要有return false and value

let authors = {
    allAuthors: {
    fiction: [
    'Agatha Christie',
    'J. K. Rowling',
    'Dr. Seuss'
    ],
    scienceFiction: [
    'Neal Stephenson',
    'Arthur Clarke',
    'Isaac Asimov',
    'Robert Heinlein'
    ],
    fantasy: [
    'J. R. R. Tolkien',
    'J. K. Rowling',
    'Terry Pratchett'
    ]
    }
}
// 这种结构是不能直接遍历的

为了实现特定数据结构的遍历,我们需要定制函数

let authors = {
    allAuthors: {
    fiction: [
    'Agatha Christie',
    'J. K. Rowling',
    'Dr. Seuss'
    ],
    scienceFiction: [
    'Neal Stephenson',
    'Arthur Clarke',
    'Isaac Asimov',
    'Robert Heinlein'
    ],
    fantasy: [
    'J. R. R. Tolkien',
    'J. K. Rowling',
    'Terry Pratchett'
    ]
    }
    }
authors[Symbol.iterator] = function () {
    let author = this.allAuthors
    let key = Reflect.ownKeys(author)
    let val = []
    return {
        next(){
            if(!val.length){
                if(key.length){
                    val = author[key[0]]
                    key.shift()
                }
            }
        return {
            done: !val.length,
            value: val.shift()
        }
      }
   }
}
let q=[]
for(let v of authors) {
    q.push(v)
}
console.log(q)

但是这种定义的迭代器可以复用吗

import导入

import导入机制为模块化提供了极大的遍历,文件可以灵活的根据需要导入模块

import {one ,two as what} from './1.js'1.js中
export { one, two}
import one(可以是任意名) from './2.js'2.js中
export default one

export导出可以是多次导出,但是export default只能出现一次

模块文件
export {...}
export default class People {}
导入文件
import * as mod from '..文件路径'
export 中的函数可以通过mod.函数调用
但是,export default会改写类名People,所以People这个类如果想
被调用,我们要用mod.default

ES beyond

flat/flatMap

对于多维数组,注意一定是纯数组的类型下,我们可以用flat对数组降维。注意,它的返回值是一个新数组

const one = [1,[2,3],[4,[5]]]
console.log(one.flat(3)) //1,2,3,4,5

flat中的参数为深度参数 flatMap则是map和flat的结合,在遍历所有元素后,扁平化数组

Object.fromEntries\Object.entries

通过Object.entires可以将对象转化为数组,注意对象中不能嵌套对象

通过Object.fromEntries可以将数组转化为对象,但是数组深度只能为1

去除空格

Object数据的描述符

通过这个属性,我们可以直观的看到每个对象的具体信息

清单

symbol