vue2 Object.defineProperty 和 vue3 proxy的使用和理解
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
proxy 和 Reflect
proxy 代理
Reflect 反射
<script>
// Reflect 反射将对象的基本操作暴露出来直接使用 get set has等
// 在get中return target[key] 和 return Reflect.get(target, key) 的区别?
// 案例如下info函数
/**
* Proxy 的13种监听方式
* obj: 目标对象
* handle: 具体的操作
**/
var obj = {
name: '周杰伦',
age: '18',
phoneNumber: '1888888888',
get info() {
return `姓名:${this.name} 年龄:${this.age}`
}
}
var newTarget = new Proxy(obj, {
// handler.get() 方法用于拦截对象的读取属性操作。
"get": function (oTarget, sKey, vValue) {
console.log('get', oTarget, sKey, vValue)
// 如果不通过Reflect反射进行返回,info里面的this指向的是”源对象“的数据,不是代理对象的数据,所以在获取的时候,不会触发代理打印
return oTarget[sKey]
// Reflect返回的是代理对象的属性,可以被代理对象监听到取值
return Reflect.get(oTarget, sKey, vValue)
},
// handler.set() 方法是设置属性值操作的捕获器。
"set": function (oTarget, sKey, vValue, receiver) {
console.log('set', oTarget, sKey, vValue, receiver, oTarget.hasOwnProperty(sKey))
// 当尝试设置一个新属性时,会进入这里
if (!oTarget.hasOwnProperty(sKey)) {
// 如果属性不存在,则添加一个新的属性
oTarget[sKey] = `New property: ${sKey}`;
console.log(`Property ${sKey} added.`);
} else {
// 设置属性的值
oTarget[sKey] = vValue;
}
return true; // 返回true表示设置成功
},
// handler.has() 方法是针对 in 操作符的代理方法。
"has": function (oTarget, sKey) {
console.log('has', oTarget, sKey)
},
// handler.deleteProperty() 方法用于拦截对对象属性的 delete 操作
"deleteProperty": function (oTarget, sKey) {
},
// handler.ownKeys() 方法用于拦截 Reflect.ownKeys().
"ownKeys": function (oTarget, sKey) {
},
// handler.defineProperty() 用于拦截对象的 Object.defineProperty() 操作。
"defineProperty": function (oTarget, sKey, oDesc) {
},
// 方法是 Object.getOwnPropertyDescriptor() 的钩子。
"getOwnPropertyDescriptor": function (oTarget, sKey) {
},
// handler.apply() 方法用于拦截函数的调用。
"apply": function (target, thisArg, argumentsList) { },
// 是一个代理(Proxy)方法,当读取代理对象的原型时,该方法就会被调用。
"getPrototypeOf"(target) { },
// handler.construct() 方法用于拦截 new 操作符。为了使 new 操作符在生成的 Proxy 对象上生效,用于初始化代理的目标对象自身必须具有 [[Construct]] 内部方法(即 new target 必须是有效的)。
"construct"(target, args) { },
// handler.isExtensible() 方法用于拦截对对象的 Object.isExtensible()。
"isExtensible"(target) { },
// handler.preventExtensions() 方法用于设置对Object.preventExtensions()的拦截
"preventExtensions"(target) { },
// handler.setPrototypeOf() 方法主要用来拦截 Object.setPrototypeOf().
"setPrototypeOf": function (target, prototype) { }
});
// 代理对象添加新属性,需要在set中特殊处理
newTarget.a = 10
// 某一个属性是否属于这个对象 in
console.log('a' in newTarget, newTarget)
// 改变新添加的代理属性,可以被监听到
newTarget.a = 15
console.log(newTarget.info)
</script>
<script>
// vue3使用代理Proxy案例
var star = {
name: '周杰伦',
age: '18',
phoneNumber: '1888888888'
}
/**
* star: 目标对象
* handle: 具体的操作
**/
var proxy = new Proxy(star, {
get: function (target, getPropName, proxy) {
// console.log(target, getPropName, proxy, '访问了值')
if (getPropName == 'phoneNumber') {
return '我不告诉你'
}
return target[getPropName]
},
set: function () {
}
})
// console.log(proxy.phoneNumber)
// 需求:
// var arr = [1,2,3,4]
// arr[-1] 获取到下标最后一项
function createArray(arr) {
let handle = {
get(target, index, proxy) {
index = Number(index)
if (index < 0) {
index += target.length;
}
// Reflect 反射类似于 return target[index]
return Reflect.get(target, index, proxy)
}
}
return new Proxy(arr, handle);
}
arr = createArray([1, 2, 3, 4])
// console.log(arr[-2])
</script>
<script>
// vue2 Object.defineProperty 实现数据监听
let obj1 = {
name: 'mike',
like: ['football', 'baskball'],
score: { js: 88, php: 92 }
}
//重新定义数组处理方法,添加监听 (重写array数组方法)
function reWriteArrayFn(obj) {
const arrayFn = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];
arrayFn.forEach(method => {
obj[method] = function (val) {
// 继承Array的方法
Array.prototype[method].apply(obj, arguments);
console.log(`操作数组 ${method} ${val}`);
}
})
}
// 通过defineProperty监听数据的获取和修改
function defineProp(obj, key, value) {
Object.defineProperty(obj, key, {
// value:默认值为undefined, 为对象的属性赋值
configurable: true, // 默认值为 false, 控制属性是否可以从对象中删除以及其特性(除了 value 和 writable)是否可以更改。
enumerable: true, // 默认值为 false,控制属性是否可以被遍历
// writable: false, // 默认值为 false,控制属性是被可以被修改
get() {
console.log('获取数据', value)
return value;
},
set(newVal) {
console.log('修改数据', newVal)
value = newVal
},
})
}
// 这里循环对象,通过defineProperty监听对象上的每一个属性,array对象单独处理
function mapFn(obj) {
if (typeof obj !== 'object' || obj == null) return;
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'object' && obj[key] !== null) {
if (Array.isArray(obj[key])) {
reWriteArrayFn(obj[key]);
} else {
mapFn(obj[key]);
}
};
defineProp(obj, key, obj[key]);
});
}
mapFn(obj1)
// 修改数组
obj1.like.push(111)
console.log(obj1.name)
</script>
</body>
</html>