js-day23-数据劫持

80 阅读4分钟

Object.defineProperty()

  • 数据劫持 => 在操作对象的时候,可以设置一些限定
  • 限定
    • 对象里面的属性不允许你遍历
    • 对象里面的属性不允许你修改
    • 对象里面的属性不允删除
    • ...
  • 配置项:
    • value: 该属性对应的值,
    • configurable 该属性是否能被删除
    • writable: 该属性时候可被重写, 默认是 false
    • enumerable: 该属性是否可被枚举, 默认是 false
    • !(经常用)get: 是一个函数, 叫做 getter 获取器, 可以来决定该属性的值
      • get 函数的返回值, 就是当前这个属性的值
      • 注意: 不能和 valuewritable一起使用, 会报错
    • !(经常用)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)