本文共 9464 字,大约阅读时间需要 31 分钟。
const target = {}; // 要被代理的原对象// 用于描述代理过程的handlerconst handler = { get: function (target, key, receiver) { console.log(`getting ${key}!`); return Reflect.get(target, key, receiver); }, set: function (target, key, value, receiver) { console.log(`setting ${key}!`); return Reflect.set(target, key, value, receiver); }}// obj就是一个被新的代理对象const obj = new Proxy(target, handler);obj.a = 1 // setting a!console.log(obj.a)// getting a!
obj.a = 1; // setting a!console.log(target.a) // 1 不会打印 "getting a!"
const target = {};const handler = {};const obj = new Proxy(target, handler);obj.a = 1;console.log(target.a) // 1
const target = {};const obj = {};const handler = { get: function(target, key){ console.log(`get ${key} from ${JSON.stringify(target)}`); return Reflect.get(target, key); }}const proxy = new Proxy(target, handler);Object.setPrototypeOf(obj, proxy);proxy.a = 1;obj.b = 1console.log(obj.a) // get a from {"a": 1} 1console.log(obj.b) // 1
get(target, propKey, receiver):拦截对象属性的读取,比如proxy.foo和proxy['foo'];
set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值;
has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值;
ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、
getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象;
defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值;
preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值;
getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象;
isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值;
setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截;
apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(…args)、proxy.call(object, …args)、proxy.apply(…);
construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(…args);
const obj = { _name: 'nanjin', age: 19, getName: () => { return this._name; }, setName: (newName) => { this._name = newName; }}const proxyObj = obj => new Proxy(obj, { get: (target, key) => { if(key.startsWith('_')){ throw new Error(`${key} is private key, please use get${key}`) } return Reflect.get(target, key); }, set: (target, key, newVal) => { if(key.startsWith('_')){ throw new Error(`${key} is private key, please use set${key}`) } return Reflect.set(target, key, newVal); }})const newObj = proxyObj(obj);console.log(newObj._name) // Uncaught Error: _name is private key, please use get_namenewObj._name = 'newname'; // Uncaught Error: _name is private key, please use set_nameconsole.log(newObj.age) // 19console.log(newObj.getName()) // nanjin
const defineReactiveData = data => { Object.keys(data).forEach(key => { let value = data[key]; Object.defineProperty(data, key, { get : function(){ console.log(`getting ${key}`) return value; }, set : function(newValue){ console.log(`setting ${key}`) notify() // 通知相关的模板进行编译 value = newValue; }, enumerable : true, configurable : true }) })}
const data = { name: 'nanjing', age: 19}defineReactiveData(data)data.name // getting name 'nanjing'data.name = 'beijing'; // setting name
const data = { userIds: ['01','02','03','04','05']}defineReactiveData(data);data.userIds // getting userIds ["01", "02", "03", "04", "05"]// get 过程是没有问题的,现在我们尝试给数组中push一个数据data.userIds.push('06') // getting userIds
// 获得数组原型const arrayProto = Array.prototypeexport const arrayMethods = Object.create(arrayProto)// 重写以下函数const methodsToPatch = [ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']methodsToPatch.forEach(function(method) { // 缓存原生函数 const original = arrayProto[method] // 重写函数 def(arrayMethods, method, function mutator(...args) { // 先调用原生函数获得结果 const result = original.apply(this, args) const ob = this.__ob__ let inserted // 调用以下几个函数时,监听新数据 switch (method) { case 'push': case 'unshift': inserted = args break case 'splice': inserted = args.slice(2) break } if (inserted) ob.observeArray(inserted) // 手动派发更新 ob.dep.notify() return result })})
const push = Array.prototype.push;Array.prototype.push = function(...args){ console.log('push is happenning'); return push.apply(this, args);}data.userIds.push('123') // push is happenning
当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:vm.items.length = newLength
const defineReactiveProxyData = data => new Proxy(data,{ get: function(data, key){ console.log(`getting ${key}`) return Reflect.get(data, key); }, set: function(data, key, newVal){ console.log(`setting ${key}`); if(typeof newVal === 'object'){ // 如果是object,递归设置代理 return Reflect.set(data, key, defineReactiveProxyData(newVal)); } return Reflect.set(data, key, newVal); } })const data = { name: 'nanjing', age: 19};const vm = defineReactiveProxyData(data);vm.name // getting name nanjingvm.age = 20; // setting age 20
vm.userIds = [1,2,3] // setting userIdsvm.userIds.push(1);// getting userIds 因为我们会先访问一次userids// getting push 调用了push方法,所以会访问一次push属性// getting length 数组push的时候 length会变,所以需要先访问原来的length// setting 3 通过下标设置的,所以set当前的index是3// setting length 改变了数组的长度,所以会set length// 4 返回新的数组的长度
vm.userIds.length = 2// getting userIds 先访问// setting length 在设置vm.userIds[1] = '123'// getting userIds 先访问// setting 1 设置index=1的item// "123"
const obj = {};const handler = {};const {proxy, revoke} = Proxy.revocable(obj, handler);proxy.a = 1proxy.a // 1revoke();proxy.a // Uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked
const target = new Date();const handler = {};const proxy = new Proxy(target, handler);proxy.getDate(); // Uncaught TypeError: this is not a Date object.
const target = new Date();const handler = { get: function(target, key){ if(typeof target[key] === 'function'){ return target[key].bind(target) // 强制绑定 this到原对象 } return Reflect.get(target, key) }};const proxy = new Proxy(target, handler);proxy.getDate(); // 6
转载地址:http://okfpi.baihongyu.com/