数据劫持:在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果.数据劫持最典型的应用------双向的数据绑定
defineProperty
VUE实现双向数据绑定的原理就是利用了 Object.defineProperty() 这个方法重新定义了对象获取属性值(get)和设置属性值(set)的操作来实现的。
用法:
Object.defineproperty(obj, key, descriptor)
缺陷
-
只能劫持对象已有的属性,且仅能劫持getter和setter
-
无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应
let obj = { age: 11 } let val = 1 // 初始值 Object.defineProperty(obj, 'name', { get() { console.log('get') return val }, set(newVal) { console.log('set') val = newVal } }) console.log(obj.name) // get 1 obj.name = 2 // set console.log(obj.name); // get 2
Proxy
用法
// target: 用Proxy包装的目标对象(可以是任何类型的对象,包括原生数组、函数,甚至另一个代理)
// handler:一个对象,其属性是当执行一个操作时定义代理的行为的函数
let p = new Proxy(target, handler)
例子
let obj = {
name: 'Eason',
age: 30
}
let handler = {
get (target, key, receiver) {
console.log('get', key)
return Reflect.get(target, key, receiver)
},
set (target, key, value, receiver) {
console.log('set', key, value)
return Reflect.set(target, key, value, receiver)
}
}
let proxy = new Proxy(obj, handler)
proxy.name = 'Zoe' // set name Zoe
proxy.age = 18 // set age 18
Reflect是一个内置的对象, 它提供拦截JavaScript操作的方法。这些方法与处理器对象的方法相同。Reflect不是一个函数对象,因此它是不可构造的。Reflect的所有属性和方法都是静态的(如Math对象)。
- Reflect.get():获取对象身上某个属性的值,类似于 target[name]。
- Reflect.set():将值分配给属性的函数,返回一个Boolean,如果更新成功,则返回true
proxy返回的是一个新对象, 可以通过操作返回的新的对象打到目的
- 劫持一个未来的(新增的)属性
- 元编程(对编程语言的再次编程),可劫持
- 对象(delete getOwnPropertyNames...等)
- 数组
- 方法
- Proxy是es6提供的新特性,兼容性不好,最主要的是这个属性无法用polyfill来兼容
例子
function asyncQue(cb) {
setTimeout(() => {
cb()
}, 1000)
}
const cb = () => {
console.log(123)
}
// 一秒后同时执行
asyncQue(cb);
asyncQue(cb);
asyncQue(cb);
// 劫持函数
// Reflect 反射
// promise 代表上一个任务
let asyncQueProxy = new Proxy(asyncQue, {
// 调用asyncQue函数
apply(target, ctx, args) {
// target: asyncQue
// args: 参数数组; ctx: this
promise = Promise.resolve(promise).then(() => {
return new Promise((resolve, reject) => {
// 恢复函数的默认行为
// args: cb
Reflect.apply(target, ctx, [() => {
args[0]()
resolve()
}])
})
})
}
})
asyncQueProxy(cb); // 1秒后执行
asyncQueProxy(cb); // 2秒后执行
asyncQueProxy(cb); // 3秒后执行
详情请看数据劫持 OR 数据代理