Proxy代理

一笑奈何 提交于 2020-04-06 05:01:46

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)
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!