数据劫持
什么是数据劫持
以原始数据为基础, 对数据进行一份复刻
特点:复刻出来的数据是不允许修改的, 值从原始数据里面获取
语法:
Object.defineProperty('哪一个对象' , '属性' , {'配置项'}')
配置项里的属性:
value : 这个属性的对应值
writable : 该属性能否被修改 , 默认为false 不能修改
enumerable : 该属性能不能被枚举(遍历), 默认为false 不能枚举.
get : 是一个函数, 叫做 getter获取器 可以决定当前属性的值 , 不能与 value 和 writable 同时出现
set : 是一个函数 , 叫做setter设置器, 当你急需要修改属性值的时候会触发.
const obj = {}
obj.name = '李四'
Object.defineProperty(obj, 'age', {
//value与writable不能与get 同时出现
// value: 18,
// writable : true,
enumerable: true,
get (){
console.log('访问get函数 , 获取obj的age属性, 然后这个函数内可以做很多事')
return 300
},
set (Val){
console.log('修改obj的age属性 , 所以触发了setter 函数, 你想要设置的值为', Val)
}
},)
console.log(obj)
obj.age = 99
//修改age的值此时会运行 set 此时控制台会打印 '修改obj的age属性 , 所以触发了setter 函数, 你想要设置的值为', 99 这串字符串
如何进行数据劫持
基础版
//获取元素
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 = 100
box2.innerHTML = `obj的年龄:${obj.age}`
console.log(res)
console.log(obj)
优化版
//原是对象
const obj = {}
obj.name = '李四'
obj.age = 18
function observer(origin, callback) {
//创建一个对象 , 将 origin 内部的属性劫持到这个对象内
const target = {}
//劫持 origin 上的属性 到 target 上
for (let key in origin) {
Object.defineProperty(target, key, {
enumerable: true,
get() {
return origin[key]
},
set(Val) {
origin[key] = Val
callback(target)
}
})
}
//最后将劫持到的 target 返回出去
return target
}
const newObj = observer(obj, fn)
console.log(newObj)
function fn(res) {
box.innerHTML = `姓名:${res.name} , 年龄:${res.age}`
}
升级版
//原对象
const obj = {}
obj.name = '李四'
obj.age = 18
//自己劫持自己
for (let key in obj) {
Object.defineProperties(obj, {
//为了和原属性相同,所以会在原本的属性名前加一个下划线,用来区分
['_' + key]: {
value: obj[key],
writable: true
},
[key]: {
get() {
return obj['_' + key]
},
set(Val) {
obj['_' + key] = Val
box2.innerHTML = `res对象的姓名:${obj.name},年龄:${obj.age}`
}
}
})
}
//页面首次打开时渲染页面
box2.innerHTML = `res对象的姓名:${obj.name},年龄:${obj.age}`
数据代理
是官方给的名字, 有部分程序员还是习惯性的叫做 数据劫持
proxy 是ES6 以后官方推出的, 是一个内置函数
const obj = {
name: '李四',
age: 18
}
//new proxy 第一个参数为 要代理的对象 , 第二个参数 一些配置项, 最后返回一个代理后的对象,我们需要使用一个变量去接收.
const res = new Proxy(obj, {
get(target, property) {
/**
* 第一个形参 : 就是你要代理的对象, 在当前案例中指的是 obj
*
* 第二个参数 : 就是该对象内部的某一个属性, 自动分配
*
*/
return target[property]
},
Set(target, property, val) {
target[property] = val
}
})
在代理完成后给原始对象新加一个属性,此时代理对象依然可以访问到( proxy 独有功能 )
回调地狱
什么是回调地狱
回调地狱不是我们写代码时出现的漏洞,只是我们在利用回调函数解决问题的时候,代码多了的一种视觉体验
例如:
function fn (chenggong , shibai) {
const timer = Math.ceil(Math.random()*3000)
console.log('我去买水')
setTimeout(() => {
if(timer > 2000){
console.log('买水失败,用时',timer)
shibai()
}else{console.log('买水成功,用时',timer)
chenggong()
}
}, timer);
}
fn(
() => {
fn(
() => { console.log('班长买完水后, 又买了一箱饮料') },
() => { console.log('班长就买了一瓶水, 他不愿意给你们买饮料') }
)
},
() => {
fn(
() => { console.log('班长第二次买水 成功了') },
() => {
fn(
() => { console.log('班长第三次买水 成功了') },
() => { console.log('班长第三次买水 又失败了, 确实不争气') }
)
}
)
}
)
缺点:不利于后期代码的阅读,与维护