标签:搜索 并且 next doc 实时 href mat 例子 isp
Proxy 拦截器
Proxy的原意是“拦截”,可以理解为对目标对象的访问和操作之前进行一次拦截。提供了这种机制,所以可以对目标对象进行修改和过滤的操作。
const proxy = new Proxy({}, {
get(target, proper(Key) {
console.log(‘你的访问被我拦截到了‘)
return 1;s
},
set(target, properKey, properValue) {
console.log(‘你修改这个属性被我拦截到了‘)
}
})
Proxy 实际上重载了点运算符,即用自己的定义覆盖了语言的原始定义。
语法:
const proxy = new Proxy(target, hanlder)
new Proxy生成一个 proxy的实例, target表示要拦截的目标,可以是对象或函数等。凡是对目标对象的一些操作都会经过拦截器的拦截处理。 hanlder 参数也是一个对象,它表示拦截配置,就如上例所示。
Proxy
实例也可以作为其他对象的原型对象。对这个目标对象进行操作,如果它自身没有设置这个属性,就会去它的原型对象上面寻找,从而出发拦截行为。如下例:
const proxy = new Proxy({}, {
get(target, key) {
consoloe.log(`你访问的属性是${key}`)
}
})
const newObject = Object.create(proxy)
newObject.a // 你访问的属性是a
提醒
同一个拦截器可以拦截多个操作,只需要在第二个参数(hanlder)配置添加
如果对这个目标对象没有设置拦截行为,则直接落在目标对象上。
Proxy 支持的拦截操作
propkey in proxy
delete proxy[propKey]
get(target, key): 当访问目标对象属性的时候,会被拦截。
target
: 目标对象
key
: 访问的key值
const proxy = new Proxy({a:1,b:2}, {
get(target, key) {
console.log(‘called‘)
return target[key]
}
})
proxy.a // 1
// called 会被打印出来
上面的代码中,当读取代理对象属性的时候,会被get方法拦截。所以可以在拦截前做一些事情,比如必须访问这个对象存在的属性,如果访问对象不存在的属性就抛出错误! 如下例:
const obj = {
name: ‘qiqingfu‘,
age: 21
}
const proxy = new Proxy(obj, {
get(target, key) {
if (key in target) {
return target[key]
} else {
throw Error(`${key}属性不存在`)
}
}
})
以上代码读取代理对象的属性,如果存在就正常读取,负责提示错误访问的key值不存在。
如果一个属性不可配置(configurable), 或者不可写(writeble),则该属性不能被代理
const obj = Object.defineProperties({}, {
foo: {
value: ‘a‘,
writeble: false, // 不可写
configurable: false, //不可配置
}
})
const proxy = new Proxy(obj, {
get(target, key) {
return ‘qiqingfu‘
}
})
proxy.value // 报错
通过get()方法可以实现一个函数的链式操作
const pipe = (function(){
return function (value) {
const funcStack = []; // 存放函数的数组
const proxy = new Proxy({}, {
get(target, fnName) {
if (fnName === ‘get‘) {
return funcStack.reduce((val, nextfn) => {
return fn(val)
}, value)
}
funcStack.push(window[fnName])
return proxy //返回一个proxy对象,以便链式操作
}
})
return proxy
}
}())
var add = x => x * 2;
var math = y => y + 10;
pipe(3).add.math.get // 16
set(target, key, value)方法用于拦截某个属性的赋值操作
target
: 目标对象
key
: 要设置的key值
value
: 设置的value值
返回值
: Boolean
假如有一个prosen对象,要设置的值不能小于100,那么就可以使用 set
方法拦截。
const prosen = {
a: 101,
b: 46,
c: 200
}
const proxy = new Proxy(prosen, {
set(target, key, value) {
if (value < 100) {
throw Error(`${value}值不能小于100`)
}
target[key] = value
}
})
上面代码对prosen对象赋值,我们可以拦截判断它赋值如果小于100就给它提示错误。
使用场景
get
和set
, 如下例规定对象的内部属性以_开头的属性不能进行读写操作。
const obj = {
name: ‘qiqingfu‘,
age: 21,
_money: -100000,
_father: ‘xxx‘
}
function isSeal(key) {
if (key.charAl(0) === ‘_‘) {
return true
}
return false
}
const proxy = new Proxy(obj, {
get(target, key) {
if (isSeal(key)) {
throw Error(`${key},为内部属性,不可以读取`)
}
return target[key]
},
set(target, key, value) {
if (isSeal(key)) {
throw Error(`${key},为内部属性,不可以修改`)
}
target[key] = value
return true
}
})
以上代码obj对象设置了内部属性,以_开头的不支持读写。那么可以使用Proxy对其进行拦截判断。get和set中的key属性如果是以_开头的属性就提示错误。 set
方法修改完值后,返回的是一个布尔值。 true成功,反则false为修改失败。
apply(target, context, args) 方法可以拦截函数的调用,call()、apply()
target
: 目标对象,
context
: 目标对象的上下文对象
args
: 函数调用时的参数数组
const proxy = new Proxy(function(){}, {
apply(target, context, args) {
console.log(target, ‘target‘)
console.log(context, ‘context‘)
console.log(args, ‘args‘)
}
})
const obj = {
a: 1
}
proxy.call(obj,1,2,3)
上面的代码是拦截一个函数的执行,分别打印:
target -> function(){}
: 目标对象
context -> {a: 1}
: 目标对象的上下文对象,也就是函数的调用者,这里我们使用call
,让obj对象来调用这个函数。
args -> [1,2,3]
: 目标对象函数调用时我们传递的参数,这里会以数组的形式接受。
例子:
再说下面一个例子之前,先了解一下Reflect.apply()
, 下面是 MDN
的解释
Reflect.apply() 通过指定的参数列表发起对目标(target)函数的调用。
语法: Reflect.apply(target, context, args)
target
: 目标函数
context
: 目标函数执行的上下文
args
: 函数调用时传入的实参列表,该列表应该是一个类数组的对象
该方法和ES5的 function.prototype.apply()
方法类似。
下面对 sum
函数的调用进行拦截,并且将函数的执行结果 *2
const sum = (num1, num2) => {
return num1 + num2
}
const proxy = new Proxy(sum, {
apply(target, context, args) {
// 我们可以通过 Reflect.apply()来调用目标函数
return Reflect.apply(...arguments) * 2
}
})
proxy(3,4) // 14
以上代码是对 sum
函数进行代理,并且将其执行结果 * 2
has(target, key ) 方法即拦截 hasProperty操作, 判断对象是否具有某个属性时,这个方法会生效。
target
: 目标对象,
key
: 对象的属性
返回值是一个布尔值
如果原对象不可配置或者禁止扩展, 那么has拦截会报错。 for in循环虽然也有 in 操作符,但是has对 for in 循环不生效.
has在什么情况下会进行拦截:
例1:
使用 has方法隐藏属性,使其不被 in
操作符发现。 就比如说对象以_开头的属性不能被发现。
const prosen = {
name: ‘qiqingfu‘,
_age: 21
}
const proxy = new Proxy(prosen, {
has(target, key) {
if (key.chatAt(0) === ‘_‘) {
return false
}
return key in target
}
})
例2: with检查
with的定义总结
在with语句块中,只是改变了对变量的遍历顺序,由原本的从执行环境开始变为从with语句的对象开始。当尝试在with语句块中修改变量时,会搜索with语句的对象是否有该变量,有就改变对象的值,没有就创建,但是创建的变量依然属于with语句块所在的执行环境,并不属于with对象。
with语句接收的对象会添加到作用域链的前端并在代码执行完之后移除。
let a = ‘global a‘
const obj = {
a: 1,
b: 2
}
const fn = key => {
console.log(key)
}
const proxy = new Proxy(obj, {
has(target, key) {
console.log(target, ‘target‘)
console.log(key, ‘key‘)
}
})
with(proxy) {
fn(‘a‘)
}
//依此打印
// {a: 1, b: 2} target
// fn key
// a
以上代码是对obj对象进行代理, 通过with
检查, 访问代理对象的 a
属性会被 has
方法拦截。那么拦截的第一个target
就是目标对象, 而第二个参数key
是访问 a
时的with语句块所在的执行环境。
construct(target, args) 方法用于拦截 new 命令。
target
: 目标函数,
args
: 构造函数的参数对象
返回值必须是一个 对象
, 否则会报错。
const proxy = new Proxy(function() {}, {
construct(target, args) {
console.log(target, ‘target‘)
console.log(args, ‘args‘)
return new target(args)
}
})
new proxy(1,2)
// function() {} ‘target‘
// [1,2] ‘args‘
如果返回值不是对象会报错
deleteProperty(target, key) 拦截对象的 delete操作
target
: 目标对象
key
: 删除的哪个key值
返回值:
布尔值, true
成功,false
失败
目标对象不可配置(configurable)属性不能被deleteProperty删除, 否则会报错
const obj = Object.defineProperties({}, {
a: {
value: 1,
configurable: false,
},
b: {
value: 2,
configurable: true
}
})
const proxy = new Proxy(obj, {
deleteProperty(target, key) {
delete target[key]
return true;
}
})
delete proxy.a // 报错
delete proxy.b // true
以上代码拦截 obj
对象, 当进行删除不可配置的属性a
时,会报错。删除b
属性时则成功。
应用场景:
我们可以指定内置属性不可被删除。如以_开头的属性不能被删除
const obj = {
_a: ‘a‘,
_b: ‘b‘,
c: ‘c‘
}
const proxy = new Proxy(obj, {
deleteProperty(target, key) {
if (key.charAt(0) === ‘_‘) {
throw Error(`${key}属性不可被删除`)
return false
}
delete target[key]
return true
}
})
defindProperty(target, key, descriptor)方法拦截Object.defindProperty()操作
target
: 目标对象,
key
: 目标对象的属性
descriptor
: 要设置的描述对象
返回值
: 布尔值, true
添加属性成功, false
则会报错
const proxy = new Proxy({}, {
defineProperty(target, key, descriptor) {
console.log(target, ‘target‘)
console.log(key, ‘key‘)
console.log(descriptor, ‘descriptor‘)
return true
}
})
Object.defineProperty(proxy, ‘a‘, {
value: 1
})
以上代码是拦截一个对象的Object.defindProperty()
添加属性的操作, 如果返回值为true,表示添加成功。返回值false则会报错。
以上代码的执行结果:
getPrototypeOf(target) 方法,用来拦截获取对象原型。
target
: 代理对象
可以拦截一下获取原型的操作:
Object.prototype.isPrototypeOf() 方法
检测一个对象的原型链上有没有这个对象
语法: Objectactive.isPrototypeOf(object), 检测object
对象的原型链上有没有Objectactive
这个对象, 如果有返回true
, 否则返回false
const Objectactive = {a: 1}
const object = Object.create(Objectactive)
Objectactive.isPrototypeOf(object) // true
以上代码 Objectactive
作为 object
的原型对象,然后通过 isPrototypeOf
检测object
对象的原型链上有没有Objectactive
这个对象。 理所当然返回 true
使用 getPrototypeOf()拦截
const Objectactive = {a: 1}
const object = Object.create(Objectactive)
const proxy = new Proxy(object, {
getPrototypeOf(target) {
console.log(target, ‘target‘)
return Object.getPrototypeOf(target)
}
})
let bl = Objectactive.isPrototypeOf(proxy)
console.log(bl)
// 依此打印结果:
/*
{
__proto__:
a: 1,
__proto__: Object
} ‘target‘
true
*/
以上代码对 object
对象进行代理,当访问原型对象时,通过getPrototypeOf()
方法拦截,target
就是代理对象。
isExtensible(target)
标签:搜索 并且 next doc 实时 href mat 例子 isp
原文地址:https://www.cnblogs.com/qiqingfu/p/9978659.html