Proxy
了解Proxy
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。 new Proxy(target, handler)
target。要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。 handler。一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理的行为。handler 对象是一个容纳一批特定属性的占位符对象。它包含有 Proxy 的各个捕获器。所有的捕捉器是可选的。如果没有定义某个捕捉器,那么就会保留源对象的默认行为。
get与set
get
当通过proxy去读取对象里面的属性的时候,会进入到get钩子函数里面 get(target, property, receiver) {}
this上下文绑定在handler对象上 target。目标对象 property。被获取的属性名 receiver。Proxy或者继承Proxy的对象 get方法可以返回任何值
set
当通过proxy去为对象设置修改属性的时候,会进入到set钩子函数里面 set(target, property, receiver) {}
this上下文绑定在handler对象上 target。目标对象 property。将被设置的属性名或 Symbol value。新属性值 receiver。最初被调用的对象。通常是 proxy 本身,但 handler 的 set 方法也有可能在原型链上,或以其他方式被间接地调用(因此不一定是 proxy 本身) set() 方法应当返回一个布尔值。
返回 true 代表属性设置成功。 在严格模式下,如果 set() 方法返回 false,那么会抛出一个 TypeError 异常。
const targetObj = {
name: "小陈" ,
age: 24
} ;
let obj = new Proxy ( targetObj, {
get ( target, property, receiver) {
console. log ( "触发了get" ) ;
return target[ property] ;
} ,
set ( target, property, value, receiver) {
target[ property] = value;
console. log ( "触发了set" , target[ key] ) ;
return true ;
}
} )
console. log ( obj. name) ;
obj. name = "小王" ;
obj. age = 23 ;
console. log ( obj) ;
proxy与Object.defineProperty对比
Proxy代理的是整个对象,而不是对象的某个特定属性,不需要我们通过遍历来逐个进行数据绑定,因此解决了Object.defineProperty的不足:
对于一个对象,不需要要遍历来对所有属性监听 对于对象的新增属性,也能监听 对于数组通过push、unshift等操作方法和索引操作,也能监听 访问不存在的属性,仍然可以被get拦截到
对象监听
const person = {
name: '小陈' ,
age: 24 ,
friends: {
name: '小王' ,
age: 23 ,
}
} ;
let proxyObj = new Proxy ( person, {
get ( target, property) {
return target[ property]
} ,
set ( target, property, value) {
target[ property] = value
return true
}
} ) ;
console. log ( proxyObj. name) ;
proxyObj. age = 25 ;
console. log ( proxyObj. age) ;
proxyObj. height = '170cm' ;
console. log ( proxyObj. height) ;
console. log ( proxyObj. sex) ;
console. log ( proxyObj. friends. name) ;
proxyObj. friends. age = 24 ;
console. log ( proxyObj. friends. age) ;
proxyObj. friends. height = '165cm' ;
console. log ( proxyObj. friends. height) ;
console. log ( proxyObj. friends. sex) ;
数组监听
const hobby = [ '睡觉' ] ;
let proxyArr = new Proxy ( hobby, {
get ( target, property) {
return target[ property]
} ,
set ( target, property, value) {
console. log ( '触发了set' ) ;
target[ property] = value
return true
}
} ) ;
proxyArr. push ( '干饭' ) ;
proxyArr[ 3 ] = '扣脚' ;
console. log ( proxyArr) ;
Proxy的其他捕获器
has
针对 in 操作符的代理方法,当使用in判断属性是否在proxy代理对象里面时,会触发has has(target, prop) {}
target。目标对象 prop。需要检查是否存在的属性. has 方法返回一个 boolean 属性的值
const targetObj = {
name: "小陈" ,
age: 24
} ;
let obj = new Proxy ( targetObj, {
has ( target, prop) {
console. log ( "触发了has" ) ;
if ( prop === 'age' ) return false
return target[ prop] ;
} ,
} )
console. log ( 'name' in obj) ;
console. log ( 'age' in obj) ;
getPrototypeOf
当读取代理对象的原型时,该方法就会被调用 getPrototypeOf(target) {}
getPrototypeOf 方法的返回值必须是一个对象或者 null。
const obj = { } ;
const proto = { } ;
const p = new Proxy ( obj, {
getPrototypeOf ( target) {
console. log ( target === obj) ;
console. log ( this === handler) ;
return proto;
}
} ) ;
console. log ( Object. getPrototypeOf ( p) === proto) ;
5 种触发 getPrototypeOf 代理方法的方式
const obj = { } ;
const p = new Proxy ( obj, {
getPrototypeOf ( target) {
return Array. prototype;
}
} ) ;
console. log (
Object. getPrototypeOf ( p) === Array. prototype,
Reflect. getPrototypeOf ( p) === Array. prototype,
p. __proto__ === Array. prototype,
Array. prototype. isPrototypeOf ( p) ,
p instanceof Array
) ;
2种情况下的异常
const obj = { } ;
const p = new Proxy ( obj, {
getPrototypeOf ( target) {
return "foo" ;
}
} ) ;
Object. getPrototypeOf ( p) ;
const obj = Object. preventExtensions ( { } ) ;
const p = new Proxy ( obj, {
getPrototypeOf ( target) {
return { } ;
}
} ) ;
Object. getPrototypeOf ( p) ;
setPrototypeOf
主要用来拦截 Object.setPrototypeOf() setPrototypeOf(target, prototype) {}
target。被拦截目标对象. prototype。对象新原型或为null. 如果成功修改了[[Prototype]], setPrototypeOf 方法返回 true,否则返回 false 如果不想为对象设置一个新的原型,setPrototypeOf方法可以返回false,也可以抛出异常
const newProto = { } , target = { } ;
const p1 = new Proxy ( target, {
setPrototypeOf ( target, newProto) {
return false ;
}
} ) ;
Object. setPrototypeOf ( p1, newProto) ;
Reflect. setPrototypeOf ( p1, newProto) ;
const newProto = { } , target = { } ;
const p2 = new Proxy ( target, {
setPrototypeOf ( target, newProto) {
throw new Error ( 'custom error' ) ;
}
} ) ;
Object. setPrototypeOf ( p2, newProto) ;
Reflect. setPrototypeOf ( p2, newProto) ;
isExtensible
isExtensible() 方法用于拦截对对象的Object.isExtensible()
preventExtensions
方法用于设置对Object.preventExtensions()的拦截
getOwnPropertyDescriptor
是 Object.getOwnPropertyDescriptor() 的钩子
defineProperty
用于拦截对对象的 Object.defineProperty() 操作
deleteProperty
ownKeys
apply
construct
用于拦截new 操作符,为了使new操作符在生成的Proxy对象上生效,用于初始化代理的目标对象自身必须具有[[Construct]]内部方法(即 new target 必须是有效的)