2023-03-12 深入JavaScript高级语法六——Proxy-Reflect

本文介绍了JavaScript中监听对象操作的两种方式,Object.defineProperty和Proxy,以及ReflectAPI的作用和使用,重点讲解了Proxy如何代理对象操作并监听各种事件,包括基本操作、函数对象的监听和Reflect与Proxy的结合应用。
摘要由CSDN通过智能技术生成

1、监听对象的操作方式一——Object.defineProperty

let obj = {
    name: 'why',
    age: 18
}

Object.keys(obj).forEach(key => {
    let value = obj[key]

    Object.defineProperty(obj, key, {
        get: function() {
            console.log(`监听到obj对象的${key}属性被访问了`);
            return value
        },
        set: function(newValue) {
            console.log(`监听到obj对象的${key}属性被设置了`);
            value = newValue
        }
    })
})

obj.name = 'lisi'
obj.age = 30

console.log(obj.name);
console.log(obj.age);

缺点:

  • 首先,Object.defineProperty设计的初衷,不是为了去监听截止一个对象中所有的属性的。我们在定义某些属性的时候,初衷其实是定义普通的属性,但是后面我们强行将它变成了数据属性描述符。
  • 其次,如果我们想监听更加丰富的操作,比如新增属性、删除属性,那么Object.defineProperty是无能为力的。
  • 所以我们要知道,存储数据描述符设计的初衷并不是为了去监听一个完整的对象。

2、监听对象的操作方式二——Proxy

在ES6中,新增了一个Proxy类,这个类从名字就可以看出来,是用于帮助我们创建一个代理的:

  • 也就是说,如果我们希望监听一个对象的相关操作,那么我们就可以先创建一个代理对象(Proxy对象);
  • 之后对该对象的所有操作,都是通过代理对象来完成,代理对象可以监听我们想要对原对象进行哪些操作。

2.1、 基本操作

const obj = {
    name: 'why',
    age: 18
}

const objProxy = new Proxy(obj, {
    // 获取值时的捕获器
    get: function(target, key) {
        return target[key]
    },
    // 设置值时的捕获器
    set: function(target, key, newValue) {
        target[key] = newValue
    },
    // 监听in的捕获器
    has: function(target, key) {
        console.log(`监听到对象的${key}属性in操作`, target);
        return key in target
    }
})

objProxy.name = 'lisi'
objProxy.age = 30

console.log(obj.name); // lisi
console.log(obj.age); // 30

// in操作符
console.log('name' in objProxy); // true

2.2、Proxy对函数对象的监听

function foo() {}

const fooProxy = new Proxy(foo, {
    apply: function(target, thisArg, argArray) {
        console.log('对foo函数进行了apply调用');
        return target.apply(thisArg, argArray)
    },
    construct: function(target, argArry, newTarget) {
        console.log('对foo函数进行了new调用');
        return new target(...argArry)
    }
})

fooProxy.apply({}, ['abc', 'fff'])
new fooProxy('abc', 'ddd')

备注:Proxy详细讲解

3、Reflect

3.1、Reflect的作用

  • Reflect是ES6新增的一个API,它是一个对象,字面的意思是反射
  • 那么这个Reflect有什么用呢?
  • 它主要提供了很多操作JavaScript对象的方法,有点像Object中操作对象的方法;
    比如Reflect.getPrototypeOf(target)类似于Object.getPrototypeOf()
    比如Reflect.defineProperty(target, propertyKey, attributes)类似于Object.defineProperty()
  • 如果我们有Object可以做这些操作,那么为什么还需要有Reflect这样的新增对象呢?
  • 这是因为在早期的ECMA规范中没有考虑到这种对对象本身的操作如何设计会更加规范,所以将这些API放到了Object上面;
    但是Object作为一个构造函数,这些操作实际上放到它上面并不合适;
    另外还包含一些类似于indelete操作符,让JS看起来是会有一些奇怪的;
    所以在ES6中新增了Reflect,让我们这些操作都集中到了Reflect对象上;
  • 那么Object和Reflect对象之间的API关系,可以参考MDN文档:Reflect

3.2、Reflect和Proxy一起使用

Reflect中常见的方法和Proxy是一一对应的,也是13个。

const obj = {
    name: 'why',
    age: 18
}

const objProxy = new Proxy(obj, {
    // 获取值时的捕获器
    get: function(target, key) {
        console.log('get-----');
        return Reflect.get(target, key)
    },
    // 设置值时的捕获器
    set: function(target, key, newValue) {
        console.log('set-----');
        const result = Reflect.set(target, key, newValue)
        if (result) {
            console.log('属性更新成功');
        } else {
            console.log('属性更新失败');
        }
    },
})

objProxy.name = 'lisi'
console.log(objProxy.name);

3.3、receiver参数的作用

  • 不使用receiver参数时
let obj = {
    _name: 'why',
    get name() {
        return this._name
    },
    set name(newValue) {
        this._name = newValue
    }
}

const objProxy = new Proxy(obj, {
    get: function(target, key, receiver) {
        // receiver是创建出来的代理对象
        console.log('get方法被访问', key, receiver);
        console.log(objProxy === receiver); // true
        return Reflect.get(target, key)
    },
    set: function(target, key, newValue) {
        console.log('set方法被访问', key);
        Reflect.set(target, key, newValue)
    }
})

objProxy.name = 'lisi'
console.log(objProxy.name);

// set方法被访问 name
// get方法被访问 name Proxy {_name: 'lisi'}
// true
// lisi
  • 使用receiver参数时
let obj = {
    _name: 'why',
    get name() {
        return this._name
    },
    set name(newValue) {
        this._name = newValue
    }
}

const objProxy = new Proxy(obj, {
    get: function(target, key, receiver) {
        // receiver是创建出来的代理对象
        console.log('get方法被访问', key, receiver);
        console.log(objProxy === receiver); // true
        return Reflect.get(target, key, receiver)
    },
    set: function(target, key, newValue, receiver) {
        console.log('set方法被访问', key);
        Reflect.set(target, key, newValue, receiver)
    }
})

objProxy.name = 'lisi'
console.log(objProxy.name);

// set方法被访问 name
// set方法被访问 _name
// get方法被访问 name Proxy {_name: 'lisi'}
// true
// get方法被访问 _name Proxy {_name: 'lisi'}
// true
// lisi

3.4、Reflect中construct的作用

function Student(name, age) {
    this.name = name;
    this.age = age;
}

function Teacher() {

}

// 执行Student中的内容,但是创建出来的对象是Teacher对象
const teacher = Reflect.construct(Student, ['why', '18'], Teacher)
console.log(teacher); // Teacher {name: 'why', age: '18'}
console.log(teacher.__proto__ === Teacher.prototype); // true
  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值