1.defineProperty介绍
1.基本用法
Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性。
// obj 需要定义属性的当前对象
// prop 当前需要定义的属性名
// desc 属性描述符
Object.defineProperty(obj, prop, desc);
通过Object.defineProperty()为对象定义属性,有两种形式,且不能混合使用,
分别为数据描述符
,存取描述符
,下面分别描述两者的区别:
1)数据描述符——特有的两个属性(value, writable);
const obj = {};
Object.defineProperty(obj, "age", {
value : 18, // 默认undefind
writable : false, // 是否可以改变,默认false不可以改变
enumerable : true, // 是否可以枚举
configurable: true, // 是否可以配置
});
obj.age = 20;
console.log(obj.age); // undefined,因为writable不能改变
2)存取描述符——是由getter
、setter
函数功能来描述的属性;
Object.defineProperty(data, key, {
enumerable: true,
get() {
console.log("劫持了");
return value;
},
set(v) {
if (value === v) {
return;
}
value = v;
},
});
2.在vue2.x中的应用
有很多人说Object.defineProperty不能监听数组的变化,这种说法是不严谨的,
准确的说是不能监听到数组某些方法的变化
。
function observer(data) {
for (let prop in data) {
let value = data[prop];
Object.defineProperty(data, prop, {
enumerable: true,
get() {
console.log("数组劫持了");
return value;
},
set(v) {
if (value === v) {
return;
}
value = v;
},
});
}
}
let arr = ["张", "王", "李", "赵"];
observer(arr);
arr.pop(); //会触发get函数,控制台会打印「数组劫持了」
arr.push("周"); //不会触发,劫持失败
总结:
数据的["push", "unshift", "splice", "reverse", "sort", "pop", "shift"]
这些方法中
["push
", "unshift
", "splice
"]
是劫持不到的,在vue2.0源码中,对数组的这三个方法进行了重写,具体如下:
let arrayProto = Array.prototype;
let newProto = Object.create(arrayProto);
const arrMethods = ["push", "unshift", "splice", "reverse", "sort", "pop", "shift"];
arrMethods.forEach((method) => {
newProto[method] = function (...args) {
let inserted = null;
switch (method) {
// args 是一个数组
case "push":
inserted = args;
break;
case "unshift":
inserted = args;
break;
case "splice": //splice方法有三个参数,第三个参数才是被新增的元素
inserted = args.slice(2); //slice返回一个新的数组
break;
}
//因为inserted是一个新的数组项,所以要对数组的新增项重新进行劫持
if (inserted) ArrayObserver(inserted);
//调用数组本身对应的方法
arrayProto[method].call(this, ...args);
};
});
function observer(obj) {
...
//通过Object.defineProperty进行循环递归绑定
}
function ArrayObserver(obj) {
//对数组的新增项进行监控
obj.forEach((item) => {
observer(item);
});
}
2.Proxy介绍
1.基本用法
语法:
let p = new Proxy(target, handler);
- target:需要使用Proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理);
- handler:一个对象,其属性是当执行一个操作时定义代理的行为的函数(可以理解为某种触发器),具体的handler相关函数请查阅官网;
let arr = ["张", "王", "李", "赵"];
let testArr = new Proxy(arr, {
get(target, key) {
console.log("获取了getter属性");
return target[key];
},
});
testArr.push("周"); //获取了getter属性
3.defineProperty和Proxy对比
- Proxy性能优于Object.defineProperty。
Proxy代理的是整个对象
,Object.defineProperty只代理对象上的某个属性
,如果是多层嵌套的数据需要循环递归绑定; - 对象上定义新属性时,Proxy可以监听到,Object.defineProperty监听不到,需要借助$set方法;
- 数组的某些方法 ["push", "unshift", "splice"] 中,Object.defineProperty监听不到,Proxy可以监听到;
- Proxy在IE浏览器中存在兼容性问题。
Comments 1 条评论
ε=ε=(ノ≧∇≦)ノ