高级JS-Proxy-Promise

266 阅读6分钟

监听对象

Object.defineProperty设计的初衷,不是为了去监听截止一个对象中所有的属性的

如果我们想监听更加丰富的操作,比如新增属性、删除属性,那么Object.defineProperty是无能为力的

const obj = {
    name:'www',
    age:19
}

// 监听对象属性的操作
// 1. 监听一个属性
let _name = obj.name
Object.defineProperty(obj,'name',{
    set(newValue) {
        console.log('监听:给name设置了新的值:', newValue);
        _name = newValue
    },
    get() {
        console.log('监听:获取name的值');
        return _name
    }
})

// 2. 监听所有属性
const keys = Object.keys(obj)
for (const key of keys) {
    let value = obj[key]
    Object.defineProperty(obj,key,{
        set(newValue){
            console.log(`监听:给${key}设置了新的值:`, newValue);
            value = newValue
        },
        get(){
            console.log(`监听:获取${key}的值`);
            return value
        }
    })
}

console.log(obj.name); // www
obj.name = 'wqq'
console.log(obj.name); // wqq

Proxy基本使用

先创建一个代理对象,之后对该对象的所有操作都通过代理对象来完成,代理对象可以监听对原对象进行的哪些操作

const obj = {
    name: 'www',
    age: 19
}

// 1. 创建一个代理对象
const objProxy = new Proxy(obj,{
    /*
      target:目标对象(侦听的对象)
      property:将被设置的属性key
      value:新属性值
      receiver:调用的代理对象
    * */
    set(target,key,value){
        console.log(`监听:给${key}设置了新的值:`,value);
        target[key] = value
    },
    /*
      target:目标对象
      property:被获取的属性key
      receiver:调用的代理对象
     */
    get(target,key){
        console.log(`监听:获取${key}的值`);
        return target[key]
    }
})

// 2. 对obj的所有操作,应该去操作objProxy
console.log(objProxy.name);
objProxy.name = 'wqq'
console.log(objProxy.name);

objProxy.address = '成都' // 也可以监听
  • 其他捕获器的监听方法(了解)
const obj = {
    name: 'www',
    age: 19
}
const objProxy = new Proxy(obj, {
    deleteProperty(target, p) {
        console.log(`监听:删除${p}的值`);
        delete obj.name
    },
    has(target, p) {
        console.log(`监听:in判断${p}属性`);
        return p in target
    }
})

delete objProxy.name
console.log('age' in objProxy);
  • 监听函数对象(了解)
function foo(n1,n2) {
    console.log(this, n1, n2);
}

const fooProxy = new Proxy(foo,{
    apply(target, thisArg, argArray) {
        console.log('执行apply操作');
        target.apply(thisArg,argArray)
    },
    construct(target, argArray, newTarget) {
        console.log('监听new操作',target, argArray, newTarget);
        return new target(...argArray)
    }
})
foo.apply('aaa',[12,314])
new fooProxy('aaa','bbb')

Reflect的常见方法

在使用proxy时,不直接处理对象,让操作都集中到Reflect对象上

Reflect.set有返回布尔值,可以判断本次操作是否成功

const obj = {
    name: 'www',
    age: 19
}

const objProxy = new Proxy(obj,{
    set(target, key, value, newValue) {
        const isSuccess = Reflect.set(target,key,newValue)
        if(!isSuccess) {
            throw new Error(`set ${key} failure` )
        }
    },
})
objProxy.name = 'wqq'
console.log(obj);

Promise

  • es5之前处理异步操作的代码

我们需要自己来设计回调函数、回调函数的名称、回调函数的使用

function execCode(counter, sussesCallback, failCallback) {
    // 异步任务
    setTimeout(() => {
        if (counter > 0) {
            let total = 0
            for (let i = 0; i < 100; i++) {
                total += i
            }
            sussesCallback(total)
        } else {
            failCallback(`${counter} 值有问题`)
        }
    }, 1000)
}

execCode(100, (value) => {
    console.log('成功了', value);
}, (err) => {
    console.log('失败了', err);
})
  • promise的使用
  1. 在通过new创建Promise对象时,我们需要传入一个回调函数
  2. 这个回调函数会被立即执行,并且给传入另外两个回调函数resolve、reject
  3. 当调用resolve回调函数时,会执行Promise对象的then方法传入的回调函数
  4. 当调用reject回调函数时,会执行Promise对象的catch方法传入的回调函数

Promise使用过程,我们可以将它划分成三个状态

状态一旦被确定,就不会改变,也不能再执行某一个回调函数来改变状态

const promise = new Promise((resolve, reject) => {
    // 待定状态(pending)
    console.log('Executor代码');

    // 兑现状态(fulfilled)
    resolve()
    
    // 拒绝状态(rejected)
    reject()
})
promise.then(value => console.log('成功了'))
       .catch(err => console.log('失败了'))
       
// 这种写法也可以
promise.then(res => {
    console.log('成功了', res);
},err => {
    console.log('失败了', err);
})
function execCode(counter) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (counter > 0) {
                let total = 0
                for (let i = 0; i < 100; i++) {
                    total += i
                }
                // 成功时的回调
                resolve(total)
            } else {
                // 失败时的回调
                reject(`${counter}有问题`)
            }
        }, 1000)
    })
}

const promise = execCode(100)
promise.then((value) => {
    console.log('成功了', value);
})
promise.catch((err) => {
    console.log('失败了', err);
})

execCode(222).then(value => console.log('成功了', value))
             .catch(err => console.log('失败了', err))

then方法是返回一个新的promise,这个新的promise的决议是等到then方法传入的回调函数有返回值时,进行决议

链式中的then是在等待这个新的promise有决议后执行的

const promise = new Promise((resolve,reject) => {
    resolve('成功')
  })

  promise.then(res => {
    console.log('第一个then:',res);  // 成功
    return 'aaaa'
  }).then(res => {
    console.log('第二个then:',res); // aaa
  }).then(res => {
    console.log(res); // undefind 
  })

catch方法也会返回一个新的promise

 const promise = new Promise((resolve,reject) => {
    reject('失败')
  })

  promise.catch(err => {
    return err
  }).then(res => {
    console.log('第一个失败',res); // 失败
    return 'bbb'
  }).then(res => {
    console.log('第二个失败',res); // undefined
    return 'ccc'
  })
  • resolve值的区别
const p = new Promise((resolve) =>{
    setTimeout(() => {
        console.log('p的resolve');
    },1000)
})
const promise = new Promise((resolve, reject) => {
    // 1. 普通值(数字、字符串、对象、数组...)
     resolve(123)

    // 2. resolve(promise):当前的promise的状态会由传入的promise决定
     resolve(p) // 一秒后打印'p的resolve'

    // 3.resolve(thenable对象)
    resolve({
        name:'www',
        then(resolve) {
            resolve('1232')
        }
    })
})
promise.then(res => console.log('成功了',res))

finally实例方法

表示无论Promise对象无论变成fulfilled还是rejected状态,最终都会被执行的代码

finally()是不接收参数的

const promise1 = new Promise((resolve, reject) => {
    reject('失败')
})
promise1.then(res => console.log('成功了', res)) // 不会执行
    .catch(err => console.log('失败了', err)) // 失败了 失败
    .finally(() => console.log('执行代码')) // 执行代码

resolve、reject类方法

Promise.resolve()的用法相当于new Promise,并且执行resolve操作

Promise.reject()传入的参数无论是什么形态,都会直接作为reject状态的参数传递到catch的

const stu = []
const promise = Promise.resolve(stu)
promise.then(res => console.log('返回结果',res)) // []
// 如果某一个形参不需要用,用“_”占位
new Promise((_,reject) => {
    reject('拒绝')
}).catch(err => console.log('错误',err)) // 拒绝

all类方法

Promise.all()将多个Promise包裹在一起形成一个新的Promise

新的Promise状态由包裹的所有Promise共同决定:

  1. 当所有Promise状态为fulfilled时,新的Promise状态为fulfilled,将所有Promise返回值组成一个数组
  2. 当有一个Promise状态为reject时,新的Promise状态为reject,会将第一个reject的返回值作为参数
const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('p1 resole')
    },2000)
})
const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('p2 resole')
    },3000)
})

// 等到所有的promise都有结果了之后再回调,结果会包裹成一个数组
// 中途有reject,直接回调catch方法
Promise.all([p1,p2])
    .then(res => console.log('all promise',res))  // ['p1 resole', 'p2 resole']
    .catch( err => console.log('出错了',err)) 

allSettled类方法

Promise.allSettled()在所有的Promise都有结果,无论是fulfilled,还是rejected时,才会有最终的状态; 并且这个Promise的结果一定是fulfilled的

const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('p1 reject')
    }, 2000)
})
const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('p2 resole')
    }, 3000)
})

// 等到所有的promise都有结果,返回每一个promise的状态和结果
Promise.allSettled([p1, p2])
        .then(res => console.log('all promise', res))
//[
//  {status: 'rejected', reason: 'p1 reject'},
//  {status: 'fulfilled', value: 'p2 resole'}
//]

race类方法

Promise.race()哪个Promise有了结果,就决定最终新Promise的状态

const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('p1 reject')
    }, 2000)
})
const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('p2 resole')
    }, 3000)
})

// 谁先有结果就用谁的,只要有结果就会立马确定状态
Promise.race([p1, p2]).then(res => console.log('race resolve', res))
    .catch(err => console.log('race catch:',err))  // race catch: p1 reject

any类方法

Promise.any()方法会等到一个fulfilled状态,才会决定新Promise的状态

如果所有的Promise都是reject,也会等到所有的Promise都变成rejected状态;内部会创建一个错误信息

总结

Proxy 和 Object.defineProperty的区别

Proxy:

  1. 设计初衷就是监听对象的改变,并且提供了多种方法监听对象的操作
  2. 拦截和监视外部对对象的访问
  3. 可以直接监听数组的变化

Object.defineProperty:

  1. 设计初衷是定义对象的属性,所以有些监听操作是监听不到的
  2. 对于复杂的对象,层级很深的话,需要深度监听
  3. 删除,添加属性是不能被监听的
  4. 不能监听数组的变化

Promise的作用和使用方法

  1. 是一个对象
  2. 对象的状态不受外界影响
  3. 一旦状态改变,就不会再变
  4. 异步编程的一种解决方案,比传统的解决方案--回调函数-更加合理和更强大