前言
https://segmentfault.com/a/11...
Vue3.0应该马上就要发布正式版了。听说在新版本中,Proxy取代了Object.defineProperty
进行双向绑定。主要原因应该是Object.defineProperty
在处理数组响应是会存在缺陷。
let demo = {};
let arr = [];
Object.defineProperty(demo, "arr", {
get: function() {
return arr;
},
set: a => {
console.log("hear set");
arr = a;
},
configurable: true,
enumerable: true
});
demo.arr = [1, 2, 3, 4]; // => 'hear set';
console.log(arr === demo.arr); // => true
/* 通过索引修改数组,无法触发set */
demo.arr[2] = null; // => 没有输出hear set。
console.log(arr === demo.arr); // => true
/* 通过push,pop,sort等方法同样无法触发set */
demo.arr.push("13"); // => 没有输出hear set。
Proxy
http://es6.ruanyifeng.com/#do...
示例
Proxy
原意为代理,在实际操作中,可以理解为,在目标数据前增加一个拦截器。通过拦截器,可以实现监听、修改目标数据。
let obj = new Proxy(
{},
{
set: function(target, key, receiver) {
console.log(`set key ${key}`);
return Reflect.set(target, key, receiver);
},
get: function(target, key, receiver) {
console.log(`get key ${key}`);
return Reflect.get(target, key, receiver);
}
}
);
obj.count = 1;
// => set key count;
obj.count++;
// => get key count;
// => set key count;
构造函数
let obj = new Proxy(target, handler)
其中new Proxy
表示生成一个Proxy实例,target
为需要代理的对象,handler
则是一个对象,定义了各种代理行为。
如果handler
为空对象,访问proxy
与访问target
效果相同。
Proxy的实例方法
get()
get
方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。
var person = {
name: "张三"
};
var proxy = new Proxy(person, {
get: function(target, property) {
if (property in target) {
return target[property];
} else {
throw new ReferenceError("Property \"" + property + "\" does not exist.");
}
}
});
proxy.name // "张三"
proxy.age // 抛出一个错误
set()
set
方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。
let validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
if (value > 200) {
throw new RangeError('The age seems invalid');
}
}
// 对于满足条件的 age 属性以及其他属性,直接保存
obj[prop] = value;
}
};
let person = new Proxy({}, validator);
person.age = 100;
person.age // 100
person.age = 'young' // 报错
person.age = 300 // 报错
has()
has(target, propKey)
拦截propKey in proxy
的操作,返回一个布尔值。
deleteProperty()
deleteProperty(target, propKey)
拦截delete proxy[propKey]
的操作,返回一个布尔值。
ownKeys()
ownKeys(target)
拦截Object.getOwnPropertyNames(proxy)
、Object.getOwnPropertySymbols(proxy)
、Object.keys(proxy)
、for...in
循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()
的返回结果仅包括目标对象自身的可遍历属性。
getOwnPropertyDescriptor()
getOwnPropertyDescriptor(target, propKey)
拦截Object.getOwnPropertyDescriptor(proxy, propKey)
,返回属性的描述对象。
defineProperty()
defineProperty(target, propKey, propDesc)
拦截Object.defineProperty(proxy, propKey, propDesc)
、Object.defineProperties(proxy, propDescs)
,返回一个布尔值。
preventExtensions()
preventExtensions(target)
拦截Object.preventExtensions(proxy)
,返回一个布尔值。
getPrototypeOf()
getPrototypeOf(target)
拦截Object.getPrototypeOf(proxy)
,返回一个对象。
isExtensible()
isExtensible(target)
拦截Object.isExtensible(proxy)
,返回一个布尔值。
setPrototypeOf()
setPrototypeOf(target, proto)
拦截Object.setPrototypeOf(proxy, proto)
,返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
apply()
apply(target, object, args)
拦截Proxy
实例作为函数调用的操作,比如proxy(...args)
、proxy.call(object, ...args
)、proxy.apply(...)
。
construct()
construct(target, args)
拦截Proxy
实例作为构造函数调用的操作,比如new proxy(...args)
。
Reflect
http://es6.ruanyifeng.com/#do...
简介
Reflect
对象与Proxy
对象一样,也是 ES6 为了操作对象而提供的新 API。其目的是:
- 将
Object
对象的一些明显属于语言内部的方法(比如Object.defineProperty
),放到Reflect
对象上。 - 修改某些
Object
方法的返回结果,让其变得更合理。 - 让
Object
操作都变成函数行为。 -
Reflec
t对象的方法与Proxy
对象的方法一一对应
使用Proxy监听数组
const queuedObservers = new Set();
const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, { set });
function set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
queuedObservers.forEach(observer => observer());
return result;
}
const person = observable({
name: "Amber",
age: 18
});
function print() {
console.log(`${person.name}, ${person.age}`);
}
observe(print);
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。