代理Proxy

用于创建一个对象的代理,实现基本操作的拦截自定义,对外界的访问进行过滤与改写

const p = new Proxy(target,handler)

target:要拦截的目标对象
handler:要拦截的行为
示例

const o = { a: 1, b: 2 }
const p = new Proxy(o, {
    get(target, key, receiver) {
        console.log(`getting ${key}!`);
        return target[key]
    }
})
console.log(p.a);
setting a!
1
对象o属性读取的拦截

target可以是一个空对象,那么拦截操作会直接作用在Proxy的实例化对象

1 handler拦截操作

  1. get()
  2. set()
  3. has()
  4. deleteProperty()
  5. ownKeys()
  6. getOwnPropertyDescriptor()
  7. defineProperty()
  8. apply()
  9. getPrototypeOf()
  10. setPrototypeOf()
  11. construct()
  12. isExtensible()
  13. preventExtensions()

1.1 get(target,key,receiver)

拦截读取操作
target:目标对象
key:读取的属性名
receiver:Proxy或继承Proxy的对象

const p = new Proxy({}, {
    get(target, key, receiver) {
        console.log(key);
        console.log(target);
        console.log(receiver);
        return target[key]
    }
})
p.a = 2
console.log(p.a);

image.png

1.2 set(target,key,value,receiver)

拦截赋值操作
value:属性要赋于的值

const p = new Proxy({}, {
    set(target, key, value, receiver) {
        console.log(value);
        target[key] = value
    },
})
p.a = 2
console.log(p.a);
2
2
可以对设置的值进行判断,再决定是否进行赋值操作
在数据发生变化时,可以自动更新DOM,实现数据绑定

1.3 apply(target,object,args)

拦截函数调用,call方法、apply方法
target:目标对象(函数)
object:被调用时的上下文对象
args:调用时的参数数组

function sum(num1, num2) {
    return num1 + num2
}
const p = new Proxy(sum, {
    apply(target, obj, args) {
        console.log(obj);
        return target(...args)
    }
})
console.log(p(2, 3));
p.apply(null, [2, 3])
undefined
5
null

1.4 has(target,key)

拦截in操作 返回布尔值

const o = { _prop: "私有属性", prop: "foo" }
const p = new Proxy(o, {
    has(target, key) {
        if (key.startsWith("_")) {
            return false
        }
        return key in target
    }
})
console.log("_prop" in p);//false
console.log("prop" in p);//true
对于不可配置的属性,has拦截会报错
for...in循环可以访问到has拦截的属性

1.5 construct(target,args)

拦截new操作 返回一个对象

function Student(name, age) {
    this.name = name;
    this.age = age
}
const p = new Proxy(Student, {
    construct(target, args) {
        console.log("new操作");
        return new target(...args)
    }
})
const p1 = new p("张三", 18)
console.log(p1);
'new操作'
{name:"张三",age:18}

1.6 deleteProperty(target,key)

拦截delete操作 返回布尔值

const o = { _prop: "私有属性", prop: "foo" }
const p = new Proxy(o, {
    deleteProperty(target, key) {
        if (key.startsWith("_")) {
            return false
        }
        return delete target[key]
    }
})
console.log(delete p._prop);//false
console.log(delete p.prop);//true
拦截不可配置的属性会报错

1.7 defineProperty(target,key,descriptor)

拦截Object.defineProperty(),返回布尔值

const p = new Proxy({}, {
    defineProperty(target, key, descriptor) {
        target[key] = descriptor.value
        return true
    }
})
Object.defineProperty(p, 'b', {
    value: "0"
})
console.log(p.b);//"0"
对象不可扩展时,则不能添加目标对象不存在的属性
不可写与不可配置,不能拦截修改这两个设置

1.8 getOwnPropertyDescriptor(target,key)

拦截Object.getOwnPropertyDescriptor(),返回属性的描述对象或者undefined

const o = { _prop: "私有属性", prop: "foo" }
const p = new Proxy(o, {
    getOwnPropertyDescriptor(target, key) {
        if (key.startsWith("_")) {
            return undefined
        }
        return Object.getOwnPropertyDescriptor(target, key)
    }
})
console.log(Object.getOwnPropertyDescriptor(p, "_prop"));
console.log(Object.getOwnPropertyDescriptor(p, "prop"));
undefined
{value:"foo",writable:true,enumerable:true,configurable:true}

1.9 getProptypeOf(target)

拦截获取对象原型的操作,返回一个对象
相关操作

  1. Object.prototype.__proto__
  2. Object.prototype.isPrototypeOf()
  3. Object.getPrototypeOf()
  4. Reflect.getPrototypeOf()
  5. instanceof
function Student(name) {
    this.name = name
}
const std = new Student("张三")
let n = 1
const p = new Proxy(std, {
    getPrototypeOf(target) {
        console.log(`拦截获取原型的操作${n++}`);
        return Object.getPrototypeOf(target)
    }
})
console.log(Object.getPrototypeOf(p) === Student.prototype);
console.log(p instanceof Student);
console.log(Student.prototype.isPrototypeOf(p));
'拦截获取原型的操作1'
true
'拦截获取原型的操作2'
true
'拦截获取原型的操作3'
true

1.10 setPrototypeOf(target,proto)

拦截Object.setPrototypeOf(),返回布尔值

function Student(name) {
    this.name = name
}
const std = new Student("张三")
const p = new Proxy(std, {
    setPrototypeOf(target, proto) {
        console.log(`拦截设置原型的操作`);
        return Object.setPrototypeOf(target, proto)
    }
})
Object.setPrototypeOf(p, Object.create(null))
console.log(Object.getPrototypeOf(p));
'拦截设置原型的操作'
{}
不可扩展的对象不能修改原型

1.11 ownKeys(target)

拦截属性名的读取操作,返回一个数组
相关操作

  1. Object.getOwnPropertyNames()
  2. Object.getOwnPropertySymbols()
  3. Object.keys()
let foo = Symbol("foo")
const o = {
    a: 1,
    [foo]: "foo"
}
Object.defineProperty(o, "b", {
    value: "bar",
    enumerable: false
})
let n = 1;
const p = new Proxy(o, {
    ownKeys(target) {
        console.log(`拦截读取属性名操作${n++}`);
        return Reflect.ownKeys(target)
    }
})
console.log(Object.keys(p));
console.log(Object.getOwnPropertyNames(p));
console.log(Object.getOwnPropertySymbols(p));
'拦截读取属性名操作1'
['a']
'拦截读取属性名操作2'
['a','b']
'拦截读取属性名操作3'
[Symbol(foo)]

1.12 isExtensible(target)

拦截对象扩展性判断的操作,返回一个布尔值
相关操作

  1. Object.isExtensible()
  2. Object.isFrozen()
  3. Object.isSealed()
const o = { a: 1 }
Object.seal(o)
const p = new Proxy(o, {
    isExtensible(target) {
        console.log("拦截");
        return Object.isExtensible(target)
    }
})
console.log(Object.isExtensible(p));
console.log(Object.isFrozen(p));
console.log(Object.isSealed(p));
'拦截'
false
'拦截'
false
'拦截'
true

1.13 preventExtensions(target)

拦截修改对象扩展性的操作,返回一个布尔值
相关操作

  1. Object.preventExtensions()
  2. Object.seal()
  3. Object.freeze()
const o1 = { a: 1 }
const o2 = { b: 2 }
const p = new Proxy(o2, {
    preventExtensions(target) {
        console.log("拦截");
        if (target === o1) {
            return false
        }
        return Object.preventExtensions(target)
    }
})
Object.preventExtensions(p)
Object.seal(p)
Object.freeze(p)
console.log(Object.isExtensible(p));
拦截
拦截
拦截
false

2 Proxy上的方法与属性

Proxy.revocable(target,handler)

返回一个可取消的Proxy实例

const {proxy,revoke} = Proxy.revocable(target,handler)

proxy:Proxy的实例
revoke:取消代理的方法

const o = { a: 1 }
const { proxy:p, revoke } = Proxy.revocable(o, {})
console.log(p.a);//1
revoke()
console.log(p.a);//TypeError : Revoked
使用场景:目标对象必须通过代理才能访问,访问结束后收回代理权,不允许再次访问

反射Reflect

一个内置对象拦截操作,不是一个函数对象,不可构造
所有的属性和方法都是静态的,类似于Math对象
静态方法与Proxy的handler中的操作同名

基本与Object上的方法相同,只有细微上的差别

Reflect的静态方法

  1. get(target,name,receiver)
  2. set(target,name,value,receiver)
  3. has(obj,name)
  4. deleteProperty(obj,name)
  5. construct(target,args)
  6. getPrototypeOf(obj)
  7. setPrototypeOf(obj,proto)
  8. apply(func,thisArg,atgs)
  9. defineProperty(target,key,attr)
  10. getOwnPropertyDescriptor(target,key)
  11. isExtensible(target)
  12. preventExtensions(target)
  13. ownKeys(target)

1 get set

Object上没有这两个方法

const o = {
    a: 1,
    get b() {
        return this.foo
    },
    set b(val) {
        return this.foo = val
    }
}
const b = {
    foo: 2
}
console.log(Reflect.set(o, 'a', "1"));//true
console.log(Reflect.get(o, 'a'));//"1"
console.log(Reflect.set(o, 'b', "2", b));//true
console.log(Reflect.get(o, 'b', b));//"2"
给属性部署get与set方法,需要指定绑定的对象receiver

2 has

Reflect.has(obj,name) 等价于 name in obj

3 deleteProperty

Reflect.deleteProperty(obj,name) 等价于 delete obj[name]

4 construct

Reflect.construct(target,args) 等价于 new target(...args)

5 getPrototypeOf setPrototypeOf

Reflect.getPrototypeOf(obj) 等价于Objcet.getPrototype(obj)

前者第一个参数不是对象直接报错,后者会先转化为对象再操作

Reflect.setPrototypeOf(obj,proto) 等价于 Objcet.setPrototypeOf(obj,proto)

前者第一个参数不是对象直接报错,后者返回参数本身
第一个参数为undefined或null,两者都报错

6 apply

Reflect.apply(func,thisArg,args) 等价于 Function.prototype.apply.call(func,thisArg,args) 等价于 func.apply(thisArg,args)

相当先于把Fucntion.prototype.apply 绑定到 func上,thisArg,args作为apply 的参数
由于call绑定后会立即调用,则相当于func.call(thisArgs,args)

7 defineProperty getOwnPropertyDescriptor

Reflect.definProperty(target,key,attr) 等价于 Object.defineProperty(target,key,descriptor)

前者第一个参数不是对象,直接报错

Refeclt.getOwnPropertyDescriptor(target,key) 等价于 Object.getOwnPropertyDescriptor(target,key)

前者第一个参数不是对象报错,后者返回undefined

8 isExtensible preventExtensions

Reflect.isExtensible(target) 等价于 Objcet.isExtensible(target)

非对象,前者报错,后者返回false

Reflect.preventExtensions(target) 等价于 Objcet.prevetExtensions(target)

非对象,前者报错,后者返回原参数

9 ownKeys

Reflect.ownKeys(target) 返回对象的所有属性
等价于 Objcet.getOwnPropertyNames(target)与Objcet.getOwnPropertySymbols(target)的集合


怼怼
73 声望6 粉丝