Object.defineProperty()
- 数据劫持 => 在操作对象的时候,可以设置一些限定
- 限定
- 对象里面的属性不允许你遍历
- 对象里面的属性不允许你修改
- 对象里面的属性不允删除
- ...
- 配置项:
value: 该属性对应的值,configurable该属性是否能被删除writable: 该属性时候可被重写, 默认是 falseenumerable: 该属性是否可被枚举, 默认是 false- !(经常用)
get: 是一个函数, 叫做 getter 获取器, 可以来决定该属性的值get函数的返回值, 就是当前这个属性的值- 注意: 不能和
value和writable一起使用, 会报错
- !(经常用)
set: 是一个函数, 叫做 setter 设置器, 当你需要修改该属性的值的时候, 会触发该函数
Object.defineProperty()
- 参数1表示你要劫持那个对象
- 参数2表示要对那个属性做限定
- 参数3表示的是配置项
const obj = {
name: '张三',
age: 19
}
// 注意点:如果对象里面没有设置这个属性,那么通过劫持属性就相当于设置了一个属性,它的值是通过value配置项设置的,并且优先级更高
Object.defineProperty(obj, 'name', {
value: '王成',
configurable: false,
writable: false,
enumerable: false
})
// delete obj.name
// obj.name = '陈洁'
for(let key in obj){
console.log(key)
}
// 被劫持的对象
const obj = {}
// 声明一个对象用来给劫持的对象设置值
const objValue = {
name: '陈洁'
}
Object.defineProperty(obj, 'name', {
get(){
return objValue.name
},
set(value){
document.querySelector('h1').innerHTML = `hello,我是<span>${value}</span>,曾经江湖中的传说人物,今天晚上8点钟在直播间不见不散!`
objValue.name = value
}
})
document.querySelector('h1').innerHTML = `hello,我是<span>${obj.name}</span>,曾经江湖中的传说人物,今天晚上8点钟在直播间不见不散!`
数据劫持封装
// 设置数据的对象
const setValue = {
name: '张三',
age: 18
}
// 第二步:在进入property(setValue, render)函数之前,进行形参赋值操作
function property(setData, callback){
// 声明一个被劫持的对象,一般把这个对象称之为目标对象
const target = {}
// 遍历
// 第三步:遍历对象,遍历的是传递进来作为属性设置的对象(为什么使用对象来设置值,因为对象可以进行遍历操作,也可以避免命名冲突问题)
for(let key in setData){
// 第四步:把属性和值设置给目标对象(也就是被劫持的对象)
Object.defineProperty(target, key, {
// 第五步:把return出去的值作为target目标对象的属性值,属性是在defineProperty第二个参数的时候已经设置
get(){
return setData[key]
},
set(value){
// 第六步:修改的是传递进来作为属性设置的对象的值,因为这个值改变了,那么咱们target目标对象的值也就跟着改变了
setData[key] = value
// 第七步:当target目标对象里面的值改变了,去调用render函数
// 注意点:如果你不传递回调函数进来的话,那么callback就是一个undefined,那么&&与运算符不满足条件就不会去执行
callback && callback()
}
})
}
// 第八步:把目标对象直接返回给外界
return target
}
// 第一步:调用property(setValue, render)函数,进行参数的传递
const result = property(setValue, render)
// 第九步:property(setValue, render)函数名称加括号接收到的是返回值target对象,把target对象直接赋值给了result这个变量
// 那么当咱们修改result对象的属性值的时候,其实修改就是target目标对象的属性值。因为result和target对象存储空间是一个地址
// 因此当target对象的属性值改变了,那么result的属性值也会跟着改变,因此render在渲染的时候就会改变
function render(){
document.querySelector('h1').innerHTML = `hello,我是<span>${result.name}</span>,今年<span>${result.age}</span>岁,曾经江湖中的传说人物,今天晚上8点钟在直播间不见不散!`
}
render()
数据劫持升级
// 专门用来设置数据的
const setData = {
name: '张三',
age: 18
}
// 目标对象,被劫持的对象
const target = {}
// 使用数据劫持的升级版本
// Object.defineProperties(target, {
// 'name': {
// value: '王成',
// writable: false
// },
// 'age': {
// value: 20,
// writable: false
// }
// })
Object.defineProperties(target, {
'name': {
get(){
return setData.name
},
set(value){
console.log('hello,我被调用了')
setData.name = value
}
},
'age': {
get(){
return setData.age
},
set(value){
console.log('hello,我被调用了')
setData.age = value
}
}
})
target.name = '王成'
target.age = 20
ES6 - proxy
proxy 数据代理,就是数据劫持的封装版本的标准语法。不需要你自己去封装了,直接变成标准语法结构,内部你也不需要关注(直接用)
//
const setData = {name: '王成', age: 18}
const result = new Proxy(setData, {
// target目标对象(被劫持的对象)
// property表示被劫持对象要设置的属性(跟咱们封装数据劫持时遍历的key是一样的)
get(target, property){
return target[property]
},
// value表示你要修改目标对象的属性值
set(target, property, value){
console.log('ok 123')
target[property] = value
}
})
result.name = '张三'
console.log(result)