数据劫持
-
原因:
- 将来我们使用框架的时候(vue),框架目前都支持一个 "数据驱动视图"
- 完成数据驱动视图 需要借助 数据劫持 帮助我们完成
-
定义
- 以原始数据为基础,对数据进行一分复刻
- 复制出来的数据是不允许修改的,值从原始数据里面获取
-
语法
- Object.defineProperty('哪一个对象','属性','配置项')
- 配置项:
- value: 这个属性对应的值
- writable: 该属性是否可以被重写,默认是 false 不允许被修改
- enumerable: 该属性是否可以被枚举,默认是 false 不能被枚举到
- get: 是一个函数,叫做 getter 获取器,可以决定当前属性的值,不能与 value 与 writable 同时出现
- set: 是一个函数,叫做 setter 设置器,当你需要修改当前属性的时候,会触发该函数
-
下面是完整的数据劫持代码
<div class="box"></div>
<div class="box2"></div>
//获取元素
const box = document.querySelector('.box')
const box2 = document.querySelector('.box2')
//原始对象
const obj = {}
obj.name = '张三'
obj.age = 18
//将 obj 的属性,劫持到这个对象内
const res = {}
Object.defineProperty(res,'age',{
enumerable:true,
get () {
return obj.age
},
set (val) {
box.innerHTML = `res年龄: ${val}`
obj.age = val
}
})
res.age = 999
console.log(res)
box2.innerHTML = `obj年龄:${obj.age}`
封装数据劫持
<div class="box"></div>
<div class="box2"></div>
<script>
const box = document.querySelector('.box')
const box2 = document.querySelector('.box2')
//原始对象
const obj = {}
obj.name = '张三'
obj.age = 18
//封装数据劫持
function observer(origin,callback) {
//1.创建一个对象,将 origin 内部的属性劫持到了这个对象内
const target = {}
//2.劫持 origin 上的属性到 target 中
for(let key in origin){
Object.defineProperty(target,key,{
enumerable: reue,
get () {
return origin[key]
},
set (val) {
origin[key] = val
callback(target)
}
})
}
//99将劫持后的 target 返回出去
return target
}
const newObj = observer(obj,fn)
function fn(res){
box.innerHTML = `年龄:${res.age}; 名字:${res.name}`
}
newObj.age = 999
newObj.name = 'gg'
</script>
数据劫持升级
- 语法: Object.defineProperties('哪个对象','配置项')
<h1 class="box1"></h1>
<h1 class="box2"></h1>
<script>
//获取元素
const box1 = document.querySelector('.box1')
const box2 = document.querySelector('.box2')
//原始对象
const obj = {}
obj.name = '张三'
obj.age = 18
//将数据劫持后的对象属性存放在 res 对象中
const res = {}
//基础版
// Object.defineProperties(res,{
// age:{
// get () {
// return obj.age
// },
// set (val) {
// box2.innerHTML = `res 对象的age属性: ${val},name 属性: ${res.name}`
// obj.age = val
// }
// },
// name: {
// get () {
// return obj.name
// },
// set (val) {
// box2.innerHTML = `res 对象的age属性: ${res.age},name 属性: ${val}`
// obj.name = val
// }
// }
// })
// console.log(res)
//利用循环优化代码量
for(let key in obj) {
Object.defineProperties(res,{
[key]:{
get (){
return obj[key]
},
set (val) {
obj[key] = val
box2.innerHTML = `res 对象的 age 属性: ${res.age},name 属性: ${res.name}`
}
}
})
}
//首次打开页面,给页面做一个赋值
box1.innerHTML = `obj 对象的age属性: ${obj.age},name 属性: ${obj.name}`
box2.innerHTML = `res 对象的age属性: ${res.age},name 属性: ${res.name}`
//首次渲染完毕页面后 更改两个对象的属性值
obj.age = 666
obj.name = '李四'
res.age = 999
res.name = '锅锅'
</script>
数据劫持升级(自己劫持自己)
//获取元素he原始对象还用上面的
for (let key in obj){
Object.defineProperties(obj,{
/**
* 通常我们在处理"自己劫持自己"的时候,不会再对象的原属性上操作,而是复制出来一份一模一样的数据操作
*
* 为了和原属性名相同,所以会在原本的属性名前加一个 下划线,用来区分
*/
['_' + key]: {
value:obj[key],
writable:true
},
[key]: {
get() {
return obj['_' + key]
},
set(val) {
obj['_' + key] = val
box.innetHTML = `obj 对象的 age 属性: ${obj.age},name 属性: ${obj.name}`
}
}
})
}
//首次渲染完毕页面后 更改两个对象的属性值
obj.age = 666
obj.name = '李四'
res.age = 999
res.name = '锅锅'
数据代理
- 是官方给的名字,有部分程序员还是习惯性地叫做 数据劫持
- proxy 是 ES6以后官方推出的 是一个内置构造函数
const obj = {
name: '张三',
age: 18
}
//new Proxy 第一个参数:要代理的对象, 第二个参数:一些配置项, 最后会返回一个代理后的对象,我们需要一个变量去接收
const res = new Proxy(obj,{
get (target,property){
/**
* 第一个形参target: 就是你要代理的这个对象,在当前案例中指的就是 obj
* 第二个形参property:就是该对象内部的某一个属性,自动分配
*/
return target[property]
},
set(target,property,val){
target[property] = val
console.log(`你现在想要修改 形参 target 的属性${property},修改的值为 ${val},除此之外你还可以做很多事`)
}
})
//在代理完成后,给原始对象新加一个属性,此时代理对象依然能够访问到 (Proxy 独有的功能)
obj.abc = 'qwer'
res.age = 66
res.name = '锅锅'