数据劫持
- 将来我们在使用框架的时候(vue), 框架目前都支持一个 "数据驱动视图"完成数据驱动视图 需要借助 数据劫持帮助我们完成
- 以原始数据为基础, 对数据进行一份复刻, 复刻出来数据是不允许修改的, 值从原始数据里面获取
语法:
Object.defineProperty('哪一个对象', '属性', '配置项')
-
配置项: value: 这个属性对应的值
-
writable: 该属性是否可以被重写, 默认是 false 不允许被修改
-
enumerable: 该属性是否可以被枚举, 默认是 false 不能被枚举到
-
get: 是一个函数, 叫做 getter 获取器, 可以决定当前属性的值, 不能与 value writable 同时出现
-
set: 是一个函数, 叫做 setter 设置器, 当你需要修改这个属性的时候, 会触发该函数
const obj = {}
obj.name = '张三'
Object.defineProperty(obj, 'age', {
// value: 18,
// writable: true,
enumerable: true,
get () {
console.log('你现在访问了 obj 的 age 属性, 然后这函数内可以做很多事')
return 300
},
set (val) {
console.log('你现在想要修改 obj 的 age 属性, 所以触发了 setter 函数, 你要想要设置的值为: ', val)
}
})
obj.age = 99
console.log(obj.age)
数据劫持与渲染页面
<h1 class="box"></h1>
<h1 class="box2"></h1>
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() {
// console.log('你现在访问了 obj 的 age 属性, 然后这函数内可以做很多事')
return obj.age
},
set(val) {
// console.log('你现在想要修改 obj 的 age 属性, 所以触发了 setter 函数, 你要想要设置的值为: ', val)
box.innerHTML = `res 年龄: ${val}`
obj.age = val
}
})
res.age = 999
box.innerHTML = `res 年龄: ${res.age}`
box2.innerHTML = `obj 年龄: ${obj.age}`
obj.age = 666
res.age = 777
// box2.innerHTML = `obj 年龄: ${obj.age}`
// obj.age = 777
// box2.innerHTML = `obj 年龄: ${obj.age}`
封装数据劫持
// 将来工作中: 这个函数由框架提供, 我们直接使用即可
// 为什么要封装函数: 如果劫持的属性多了, 原本的写法不太方便, 代码量比较多, 所以封装数据劫持
function observer(origin, callback) {
// 1. 创建一个对象, 将 origin 内部的属性劫持到这个对象内
const target = {}
// 2. 劫持 origin 上的属性到 target 中
for (let key in origin) {
Object.defineProperty(target, key, {
enumerable: true,
get() {
// console.log('你现在访问了 obj 的 age 属性, 然后这函数内可以做很多事')
return origin[key]
},
set(val) {
// console.log('你现在想要修改 obj 的 age 属性, 所以触发了 setter 函数, 你要想要设置的值为: ', val)
origin[key] = val
callback(target)
}
})
}
// 3. 将劫持后的 target 返回出去
return target
}
const box = document.querySelector('.box')
const box2 = document.querySelector('.box2')
// 原始对象
const obj = {}
obj.name = '张三'
obj.age = 18
// 创建一个数据劫持后的对象
const newObj = observer(obj, fn)
function fn(res) {
box.innerHTML = `年龄: ${res.age}; 名字: ${res.name}`
}
newObj.age = 666
newObj.name = '李四'
数据劫持升级
const box = document.querySelector('.box')
// 原始对象
const obj = {}
obj.age = 18
obj.name = '张三'
// 升级版: 自己劫持自己 (全都在 原始对象上进行操作)
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.innerHTML = `obj 对象的 age 属性: ${obj.age}, name 属性: ${obj.name}`
}
}
})
}
// 首次打开页面的时候, 给页面做一个赋值
box.innerHTML = `obj 对象的 age 属性: ${obj.age}, name 属性: ${obj.name}`
// 首次渲染完毕页面后 更改对象的属性值
obj.age = 666
obj.name = '李四'