Proxy对象

在一个系统中,总要存储一些数据,对于这些数据,可能有一些是希望我们访问的,但是总有一些是中重要的,不希望我们访问的,希望保护起来,因此ES6新增了代理,在目标对象前架设个“拦截“层,外界对该对象的访问必须通过这层拦截,我们可以对外界的访问进行过滤和改写。

注意:Proxy修改了某些操作的默认行为,等同于在语言层做出修改,所以也属于”元编程“,即对语言进行编程。

语法: let proxy = new Proxy(target,handler)

target 表示被代理的对象

handler 表示操作被代理的对象

注意:[[]] 为引擎内部属性或方法,外部不能获取

let star = {
name: '尼古拉斯赵四',
girlFriend: '赵丽颖',
age: 40
}
// 代理对象
let proxy = new Proxy(star, {
// 取值方法
get(target, key, receiver) {
// 如果是重要的数据,不要访问
if (key === 'girlFriend') {
throw new Error('不能访问girlFriend')
}
if (key === 'age') {
return 0
}
// target 代表 star, key 代表属性名称, receiver代表proxy, this代表Proxy的第二个参数对象
// console.log('get', receiver === this, 11, receiver === proxy, this.num)
return target[key]
},
set(target, key, value, receiver) {
// 如果是重要的数据,不要修改
if (key === 'girlFriend') {
throw new Error('不能修改girlfriend')
}
// console.log('set', ...args, this)
// console.log(value)
target[key] = value;
},
num: 10
})
// // 修改数据
// proxy.name = '亚洲舞王'
// // 获取数据
// console.log(proxy.name)
// 修改重要数据
// proxy.girlFriend = '孙俪'
// console.log(proxy.girlFriend, star.girlFriend)
// ++star.age;
// proxy.age;
console.log(++proxy.age)


拦截方法

get(target,key,proxy) 拦截对象属性的读取

//get(target,key,proxy) 拦截对象属性的读取
// 1 对数据做校验
// 2 数组或者字符串的索引值做处理(负索引值)
// 3 方法链式调用
// 4 模拟虚拟DOM创建
// 5 configurable, writable

// 负索引值
// let arr = [1, 2, 3, 4, 5];
// let proxy = new Proxy(arr, {
// // 取值方法
// get(target, key) {
// // 如果是负索引值
// if (key < 0) {
// key = key % target.length + target.length;
// }
// return target[key]
// }
// })
// console.log(proxy[1])
// console.log(proxy[-1])

// 链式调用
// let str = 'hello ';
// console.log(str.trim().toUpperCase())
// console.log(str.trim.toUpperCase)
// 定义一个方法,包装字符串
// let ickt = function(val) {
// // 返回一个代理对象
// return new Proxy({}, {
// fns: [],
// // 取值方法
// get(target, key, proxy) {
// // 如果key是get,返回结果
// if (key === 'get') {
// // console.log(this.fns)
// // 执行对val的处理,并返回结果
// // return this.fns.reduce((result, fn) => {
// // return result[fn](result)
// // }, val)
// return this.fns.reduce((result, fn) => result[fn](result), val)
// }
// // 存储这些操作方法
// this.fns.push(key)
// // 为了链式调用,每次要返回receiver
// return proxy
// }
// })
// }
// 使用方法,省略()
// console.log(ickt(str).trim.toUpperCase.get)
// let ickt = function(val, tools) {
// // 返回一个代理对象
// return new Proxy({}, {
// fns: [],
// // 取值方法
// get(target, key, proxy) {
// // 如果key是get,返回结果
// if (key === 'get') {
// // console.log(this.fns)
// // 执行对val的处理,并返回结果
// // return this.fns.reduce((result, fn) => {
// // return result[fn](result)
// // }, val)
// return this.fns.reduce((result, fn) => (tools || result)[fn](result), val)
// }
// // 存储这些操作方法
// this.fns.push(key)
// // 为了链式调用,每次要返回receiver
// return proxy
// }
// })
// }
// // 工具方法
// let tools = {
// toUpperCase: val => val.toUpperCase(),
// repeat: val => val.repeat(2),
// trim: val => val.trim(),
// reverse: val => val.split('').reverse().join('')
// }
// // 拓展,可以传递工具集合
// console.log(ickt(str, tools).trim.repeat.reverse.toUpperCase.get)

// react jsx
// <div id="app">
// <img src="./demo.png" alt="demo">
// <p>爱创课堂</p>
// </div>
// react.createElement(
// 'div',
// { id: 'app' },
// React.createElement('img', { src: './demo.png', alt: 'demo' }),
// React.createElement('p', null, '爱创课堂')
// )
// 创建React库
// let React = new Proxy({}, {
// // 取值方法
// get(obj, name) {
// // 返回函数
// return function(attrs, ...children) {
// // 创建容器元素
// let dom = document.createElement(name);
// // 遍历属性
// attrs && Object.keys(attrs).forEach(key => dom.setAttribute(key, attrs[key]))
// // 遍历子元素
// children.forEach(item => {
// // 如果是文本
// if (typeof item === 'string') {
// // 添加文本节点
// dom.appendChild(document.createTextNode(item))
// } else {
// // 添加元素
// dom.appendChild(item)
// }
// })
// // 返回创建的元素
// return dom;
// }
// }
// })
// // // 模拟React创建DOM元素
// let result = React.div(
// { id: 'app' },
// React.img({ src: './demo.png', alt: 'demo' }),
// React.p(null, '爱创课堂')
// )
// // console.log(result)
// // 上树
// document.body.appendChild(result);

// configurable, writable
// let obj = {
// msg: 'hello'
// }
// Object.defineProperties(obj, {
// color: {
// value: 'red',
// writable: false,
// enumerable: true,
// configurable: true
// },
// num: {
// value: 20,
// writable: true,
// enumerable: true,
// configurable: false
// }
// })
// // 代理
// let proxy = new Proxy(obj, {})
// // 不传递操作方法,此时代理对象与原对象的行为是一致的
// console.log(proxy.msg)
// proxy.msg = 'ickt';
// // console.log(proxy.msg)
// console.log(obj)
// console.log(proxy.color)
// proxy.color = 'green';
// console.log(proxy.color)
// Object.defineProperty(obj, 'num', {
// value: 30,
// writable: true,
// enumerable: true,
// configurable: true
// })
// Object.defineProperty(proxy, 'num', {
// value: 30,
// writable: true,
// enumerable: true,
// configurable: true
// })
// console.log(obj)
set(target,key,value,proxy) 拦截对象属性的设置

has(target,key) 拦截propKey in proxy 的操作, 返回布尔值

deleteProperty(target,key) 拦截delete proxy[key] 操作,返回布尔值

ownKeys(target) 拦截
Object.getOwnPropertyNames,Object.getOwnPropertySymbols,Object.keys,返回值是个数组

getOwnPropertyDescriptor(target,key) 拦截 Object.getOwnPropertyDescriptor操作,返回属性值的描述对象

defineProperty(target,key,descripor) 拦截Object.defineProperty Object.defineProperties 操作。

preventExtensions(target) 拦截 Object.preventExtensions(不可拓展) ,返回布尔值

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

Object.getPrototypeOf(target) 拦截Object.getPrototypeOf(target) ,返回对象

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

Object.setPrototypeOf(target) 拦截Proxy实例 并将其作为函数调用操作

Object.construct(target,args) 拦截Proxy实例 作为构造函数调用的操作
// 定义对象
// let obj = {
// color: 'red',
// num: 10
// }
let obj = function() {
console.log('obj fn')
}
// 代理
let proxy = new Proxy(obj, {
// has(target, key) 拦截propKey in proxy 的操作,返回 个布尔值。
// has() {
// console.log(arguments)
// }
// deleteProperty(target, key) 拦截 delete proxy[key]的操作,返回 个布尔值。
// deleteProperty() {
// console.log(arguments)
// }
// ownKeys(target) 拦截Object.getOwnPropertyNames,Object.getOwnPropertySymbols, Object.keys,返回 个数组。
// ownKeys() {
// console.log(arguments)
// return []
// }
// getOwnPropertyDescriptor(target, key) 拦截Object.getOwnPropertyDescriptor操作,返回属性的描述对象。
// defineProperty(target, key, descripor) 拦截Object.defineProperty、 Object defineProperties操作
// preventExtensions(target) 拦截Object.preventExtensions (不可拓展),返回布尔值
// isExtensible(target) 拦截Object.isExtensible, 返回布尔值。
// getPrototypeOf(target) 拦截Object.getPrototypeOf,返回对象。
// setPrototypeOf(target, proto) 拦截Object.setPrototypeOf, 返回一个布尔值。
// apply(target, object, args) 拦截Proxy实例,并将其作为函数调用的操作
apply() {
console.log(arguments)
},
// construct( target, args)
construct() {
console.log('construct', arguments)
return {}
}
})
// console.log('color' in proxy)
// delete proxy.color
// console.log(Object.getOwnPropertyNames(proxy))

// 执行方法
// proxy(1, 2, 3)
// proxy.call({ color:'red' }, 1, 2, 3)
// 作为构造函数
new proxy(1, 2, 3)

 

Proxy要点

1、当操作代理对象参数是空对象的时候,对代理对象的操作将直接映射给被代理对象(无拦截)。

let obj = {}
// 代理
let proxy = new Proxy(obj, {})
// proxy与obj行为是一致的
proxy.color = 'red'
console.log(obj)//{color:"red"}

2、Proxy对目标对象的代理,是不透明的代理,因此在无拦截的情况下,也无法保证与目标对象的行为一直。原因是在Proxy代理过程中,Proxy代理对象内部this始终指向Proxy代理对象。

// let obj = {
// num: 10,
// demo() {
// console.log(this)
// }
// }
// 代理对象, 无拦截
// let proxy = new Proxy(obj, {})
// this指向 obj
// obj.demo() //obj
// proxy.demo() //proxy 代理对象

// map对象: 属性名称可以是任何数据类型。
// let map = new Map();
// // let map = {};
// // 类
// class Demo {
// constructor(color) {
// // this._color = color;
// // map[this] = color;
// map.set(this, color);
// }
// // 特性方法
// get color() {
// // 通过demo和proxy调用color属性,this指向不同。
// console.log(this, map)
// // return this._color;
// // return map[this];
// return map.get(this);
// }
// }
// // 实例化
// let demo = new Demo('red');
// // 代理对象, 无拦截
// let proxy = new Proxy(demo, {})
// // this指向 obj
// console.log(demo.color);
// console.log(proxy.color);

所以代理一些实例化对象的时候会存在this问题。 3、一些源生方法,只能通过正确的this才能获取数据,此时就无法使用Proxy做代理。

// 3 一些源生方法,只能通过正确的this才能获取数据,此时就无法使用proxy做代理了
// let r = /a/;
// let proxy = new Proxy(r, {});
// // 使用
// console.log(r.test('a'))
// console.log(proxy.test('a'))

4、Proxy.revocable方法可以返回一个可取消的代理对象。 其中revoke属性可以取消proxy代理实例。 应用场景“目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问

// 4 取消代理
let obj = {
color: 'red'
}
let { proxy, revoke } = Proxy.revocable(obj, {});
// console.log(ickt)
// 代理
console.log(proxy.color)
// 收回代理权
revoke();
// 代理失效
console.log(proxy.color)