认知
不要因为 Vue 使用到了 Object.defineProperty()
数据劫持,就把它和 Proxy 混为一谈。它俩不是一个东西
Object.defineProperty(obj, prop, descriptor)
是直接处理 obj,然后当操作 obj 时,会在 set、get 方法中进行拦截
- 对 obj 本身操作
- 给 obj 上新增没有的属性
- Proxy(target, handler) 是通过处理 obj 以后,是返回了一个代理对象,你是通过操作这个代理对象,来对数据做操作的
- 创建一个 obj 的代理,中间隔了一层交流
- 相比 defineProperty 少了个 prop 参数,因为是对已有的 obj 操作、处理
功能
自定义对象属性的获取、赋值、枚举、函数调用等功能
💄用法
getter、setter 基本用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| let target = { a: 1, b: 2 }
let proxy = new Proxy(target, { get(target, prop) { console.log('This is property value ' + target[prop]); return target[prop]; }, set(target, prop, value) { target[prop] = value; } });
console.log("proxy.a", proxy.a); console.log("target.a", target.a); proxy.b = 3; console.log("target.b", target.b); console.log("proxy.b", proxy.b); console.log(proxy);
|
处理数组、函数
- Object.defineProperty 没法直接处理数组
- Proxy 可以。函数对象和数组都行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| let arr = [ { name: 'Lance', age: 27 }, { name: 'GC', age: 31 }, { name: 'Jerry', age: 29 }, { name: 'Sherry', age: 30 } ];
let persons = new Proxy(arr, { get(arr, prop) { return arr[prop]; }, set(arr, prop, value) { arr[prop] = value; } });
persons[0] = { name: 'Xi', age: 30 } console.log(arr, persons);
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| let fn = function() { console.log('I am a function. '); }
fn.a = 123;
let newFn = new Proxy(fn, { get(fn, prop) { return fn[prop] + ' This is a Proxy return'; } });
console.log(newFn.a);
|
has
- a 不在 proxy 本身身上,是 proxy 从 target 代理来的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| let target = { a: 1, b: 2 }
let proxy = new Proxy(target, { get(target, prop) { return 'GET: ' + prop + ' = ' + target[prop]; }, set(target, prop, value) { target[prop] = value; console.log('SET: ' + prop + ' = ' + value); }, has(target, prop) { return Reflect.has(target, prop); } });
console.log('a' in proxy); console.log(proxy);
|
deleteProperty
- 经过了代理 deleteProperty 删除了 target 中的 b
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| let target = { a: 1, b: 2 }
let proxy = new Proxy(target, { get(target, prop) { return 'GET: ' + prop + ' = ' + target[prop]; }, set(target, prop, value) { target[prop] = value; console.log('SET: ' + prop + ' = ' + value); }, has(target, prop) { return Reflect.has(target, prop); }, deleteProperty(target, prop) { delete target[prop]; console.log(1); } });
delete proxy.b; console.log("proxy", proxy); console.log("target", target);
|
ES - 14种对象操作方法
获取原型 [[GetPrototypeOf]]
- 通过原型方法: Object.getPrototypeOf(obj)
- 通过 proto : obj.proto
- 通过构造函数的原型属性: 构造函数.prototype
1 2 3 4 5
| var obj = { a: 1, b: 2 }; var proto = Object.getPrototypeOf(obj); console.log(proto); console.log(obj.__proto__); console.log(Object.prototype);
|
设置原型 [[SetPrototypeOf]]
- 通过 Object.setPrototypeOf(obj, xxx)
- 通过 obj.proto = xxx
- 通过 Object.prototype = xxx
1 2 3 4 5
| var obj = { a: 1, b: 2 }; Object.setPrototypeOf(obj, { c: 3, d: 4 });
console.log(obj);
|
获取对象的可扩展性 [[IsExtensible]]
Object.isExtensible() 方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。
1 2 3 4 5
| var extensible = Object.isExtensible(obj); console.log(extensible); Object.freeze(obj); var extensible2 = Object.isExtensible(obj); console.log(extensible2);
|
Object.seal
1 2 3 4 5 6 7 8 9 10 11 12
| var obj = { a: 1, b: 2 }; Object.seal(obj); obj.c = 3; console.log(obj); delete obj.a; console.log(obj); obj.b = 3; console.log(obj);
for (const key in obj) { console.log(`key: ${key} - val: ${obj[key]}`); }
|
Object.freeze
1 2 3 4 5 6 7 8 9 10 11 12
| var obj = { a: 1, b: 2 }; Object.freeze(obj); obj.c = 3; console.log(obj); delete obj.a; console.log(obj); obj.b = 3; console.log(obj);
for (const key in obj) { console.log(`key: ${key} - val: ${obj[key]}`); }
|
获取自有属性 [[GetOwnProperty]]
1 2 3
| var obj = { a: 1, b: 2 }; Object.setPrototypeOf(obj, { c: 3, d: 4 }); console.log(Object.getOwnPropertyNames(obj));
|
禁止扩展对象 [[PreventExtensions]]
1 2 3 4 5 6 7
| var obj = { a: 1, b: 2 }; Object.preventExtensions(obj); obj.c = 3; console.log(obj);
delete obj.a; console.log(obj);
|
拦截对象操作 [[DefineProperty]]
判断是否是自身属性 [[HasOwnProperty]]
1 2
| var obj = { a: 1, b: 2 }; console.log(obj.hasOwnProperty('a'));
|
获取 [[GET]]
1 2 3 4
| var obj = { a: 1, b: 2 }; console.log('c' in obj); console.log('a' in obj); console.log(obj.a);
|
设置 [[SET]]
1 2 3 4
| var obj = { a: 1, b: 2 }; obj.a = 3; obj['b'] = 4; console.log(obj);
|
删除 [[Delete]]
1 2 3
| var obj = { a: 1, b: 2 }; delete obj.a; console.log(obj);
|
枚举 [[Enumerate]]
1 2 3 4
| var obj = { a: 1, b: 2 }; for (var k in obj) { console.log(obj[k]); }
|
获取键集合 [[OwnPropertyKeys]]
1 2
| var obj = { a: 1, b: 2 }; console.log(Object.keys(obj));
|
调用函数
1 2 3 4 5 6 7 8
| function test() {} test();
test.call/apply
var obj = { a: 1, b: 2 }; obj.test = function() {}; obj.test();
|
实例化对象
1 2
| function Test() {} new Test();
|
自定义 Proxy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| function MyProxy(target, handler) { const _target = deepClone(target); Object.keys(_target).forEach(key => { Object.defineProperty(_target, key, { get() { return handler.get && handler.get(target, key); }, set(newVal) { handler.set && handler.set(target, key, newVal); } }) }); return _target;
function deepClone(org, tar) { let target = tar || {}, toStr = Object.prototype.toString, arrType = '[object Array]';
for (var key in org) { if (org.hasOwnProperty(key)) { let value = org[key]; if (typeof value === 'object' && value !== null) { if (toStr.call(value) === arrType) { target[key] = []; } else { target[key] = {}; } deepClone(value, target[key]); } else { target[key] = value; } } } return target; } }
const obj = { a: 1, b: 2 }
const proxy = new MyProxy(obj, { get(obj, prop) { console.log('GET: ' + prop + ' = ' + obj[prop]); return obj[prop]; }, set(obj, prop, newVal) { console.log('SET: ' + prop + ' = ' + newVal); obj[prop] = newVal; } });
proxy.a = 3; console.log(proxy);
|
打印的 proxy 是个空对象,是因为 new 出来的 MyProxy 中 return 的 _target ,经过 Object.defineProperty 设置属性后,在对象中不可见了(不可枚举),展开对象后可见
在构造函数中 return 一个复杂值,会覆盖默认 return 的 this。但 return 一个基本值,不会覆盖 this
总结
通过重写 Proxy 的 handler 中相应的方法,来达到修改原对象的目的。
外界每次通过 Proxy 去访问操作 target 对象的属性的时候,会经过 handler 中的相应方法,然后通过在代理的这些方法中做处理,来达到更改 target 对象的目的
间接操作 target,此方式跟 defineProperty 的拦截方式,完全不同
💄defineProperty 和 Proxy 的异同
相同
都能对数据进行“拦截”
不同
本质不同
- defineProperty 给对象增加属性用
- proxy 代理对象,通过重写 handler 方法,间接达到修改target的目的
💄defineProperty 存在的问题
数组通过 索引、修改数组length、push、pop 等一系列方式操作数组,都无法触发 setter,而 Proxy 可以触发
vue对 push、pop 等数组方法进行了重写
Reflect 反射
介绍
ES6 中定义的一个内置对象,是方法集合的容器。全局下的一个对象,不能 new
上面讲的14种方法,这里边有13种,少了一个枚举
存在的原因
放在这上面的原因是:
因为很多对象方法,以前都放在 Object 上在,但很多方法不是直接操作 Object,有可能是操作函数、或数组。
由于这样的不合理,所以专门用 Reflect 这样一个容器来装这些方法
使用
使用 Reflect 更合理,因为是用方法返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| let target = { a: 1, b: 2 }
let proxy = new Proxy(target, { get(target, prop) { return Reflect.get(target, prop); }, set(target, prop, value) { const isOk = Reflect.set(target, prop, value); if (isOk) { console.log('SET successfully'); } } });
proxy.a = 233; console.log(proxy.b); console.log(target.a);
|