事件拦截 和事件代理()

72 阅读2分钟

1.数据劫持

/**
         *  数据劫持

         *      将来我们在使用框架的时候(vue), 框架目前都支持一个 "数据驱动视图"
         *
         *      完成数据驱动视图 需要借助 数据劫持帮助我们完成
         
         *      以原始数据为基础, 对数据进行一份复刻, 复刻出来数据是不允许修改的, 值从原始数据里面获取
         *
         *      语法: Object.defineProperty('哪一个对象', '属性', '配置项')

         *          配置项:
         *              value: 这个属性对应的值
         *              writable: 该属性是否可以被重写, 默认是 false 不允许被修改
                         
         *              enumerable: 该属性是否可以被枚举, 默认是 false 不能被枚举到
         
         *              configurable :

         *              get: 是一个函数, 叫做 getter 获取器, 可以决定当前属性的值, 不能与 value writable 同时出现

         *              set: 是一个函数, 叫做 setter 设置器, 当你需要修改这个属性的时候, 会触发该函数

        */

Object.defineProperty()

语法

Object.defineProperty(obj, prop, descriptor) 

参数

  • obj 要定义属性的对象。

  • prop 要定义或修改的属性的名称或 Symbol 。

  • descriptor 要定义或修改的属性描述符。 返回值 被传递给函数的对象。

基础版

<h1 class="box"></h1>

    <h1 class="box2"></h1>
    <script>
        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 = 99  

        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}`
    </script>   

封装版 循环加封装

// 将来工作中: 这个函数由框架提供, 我们直接使用即可
// 为什么要封装函数: 如果劫持的属性多了, 原本的写法不太方便, 代码量比较多, 所以封装数据劫持

        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 = '李四'

Object.defineProperties()

可以对多条属性进行修改

基础班

        const obj = {}
        obj.age = 18
        obj.name = '张三'   

        // 基础版
         const res = {}   // 将数据劫持后的对象属性存放在 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
                 }
             },

         })

利用循环 优化代码量

const obj = {}
obj.name = '张三'
obj.age = 18
const res = {}

        for (let key in obj) {
            Object.defineProperties(res, {
                // 此处的 key 我们的需求是当一个变量使用, 如果直接写 那么会当成一个字符串, 解决方案在 key 加一个 [] 包裹起来, 当成一个变量

                [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 的修改不会影响 box1
        obj.name = '李四'


        res.age = 999   // res 的修改会触发 set 函数, set 函数内有一行代码会让 box2 更新, 所以 res 的修改会让页面重新渲染

        res.name = '王五'

升级 自己劫持自己

// 原始对象

        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 = '李四'

2.事件代理Proxy

const obj = {

      name:'张三',

      age:18

    }

    const res = new Proxy(obj,{

      get(target,prokey){

        //第一个形参 就是你要代理的这个对象,在当前案例中就是obj

        //第二个形参:Jiushi该对象内部的某一个属性,自动分配

        return target[prokey]

      },

      set(target,prokey,val){

        //val 传参

        target[prokey] = val

        console.log(`形参的值${prokey},修改的值为${val}`)

      }

    })

    res.age = 66

    //在代理i完成后给原始对象新增一个属性,此时代理对象以然能够访问到(proxy 独有的功能)

    obj.abc = 'qwer'

    console.log(res.abc)