JavaScript的Proxy进行数据拦截模仿Vue数据的操作

Object.defineProperty()

方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

简单的说就是这个可以监听这个数组的获取操作和修改操作

存取描述符*是由 getter 函数和 setter 函数所描述的属性。

一个描述符只能是这两者其中之一不能同时是两者。

参数设置

configurable

当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。

enumerable

当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。

value

该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。

writable

当且仅当该属性的 writable 键值为 true 时,属性的值,也就是上面的 value,才能被赋值运算符改变。

get

当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。

set

当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。

描述符默认值汇总

示例

    // 数据劫持
    let data = {
        message: "测试数据"
    }
    Object.defineProperty(data, 'message', {
        configurable: true,//默认 false
        enumerable: false, //默认 false
        get() {
            console.log("get...")
            return "测试数据"
        },
        set(newValue) {
            console.log("set...", newValue)
        }
    })

    data.message = "修改"
    delete data.message

proxy

对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。

整体来说和Object.defineProperty()的作用差不多 但是比它的功能更加多

常用方法

get()

用于拦截对象的读取属性操作。

约束:

var obj = {};
Object.defineProperty(obj, "a", { 
  configurable: false, 
  enumerable: false, 
  value: 10, 
  writable: false 
});

var p = new Proxy(obj, {
  get: function(target, prop) {
    return 20;
  }
});

p.a; //会抛出TypeError

set()

设置属性值操作的捕获器。

约束

deleteProperty()

用于拦截对对象属性的 delete 操作。

删除属性: delete proxy[foo]delete proxy.foo

has()

是针对 in 操作符的代理方法。

这个钩子可以拦截下面这些操作:

约束:

如果违反了下面这些规则, proxy 将会抛出 TypeError:

下面的操作违反了规则

var obj = { a: 10 };
Object.preventExtensions(obj);
var p = new Proxy(obj, {
  has: function(target, prop) {
    return false;
  }
});

'a' in p; // TypeError is thrown

示例

    // proxy
    let obj = {
        name: "张三",
        age: 20
    }
    let newObj = new Proxy(obj, {
            //获取
        get(target, key) {
            console.log("proxy1")
            return target[key]
        },
        //设置
        set(target, key, newValue) {
            target[key] = newValue
            console.log("proxy2")
            return true
        },
        //in操作
        has(target, key) {
            console.log("has")
            return true
        },
    })
    // newObj.name;
    // newObj.name = "李四"
    console.log("name" in newObj)

编写vue的数据渲染

需求

  1. 将html里面以 {{ }} 的进行替换
  2. 多层div嵌套式替换
  3. 将标签的进行替换 v-html
  4. 将v-model 为实时替换

整体就是模仿vue 的数据操作但是肯定会有很多bug 基础的东西可以了

 <div id="app">
        {{message}}
        <div>
            <h1>{{message}}</h1>
        </div>
        <div v-html="htmlData"></div>
        <input v-model="modelData" value="" /> {{modelData}}
  </div>
    let vm = new Kvue({
        el: '#app',
        data: {
            message: "测试数据",
            htmlData: "html数据",
            modelData: "绑定数据"
        }
    })
    setTimeout(() => {
        vm.$options.data.message = "修改数据"
    }, 1000)

完整代码

    class Kvue extends EventTarget {
        constructor(options) {
            super()
            this.$options = options
            this.compile()
            this.observe(this.$options.data)
        }
        observe(data) {
            let keys = Object.keys(data)
            keys.forEach(key => {
                this.defineProperty(data, key, data[key])
            })
        }
        defineProperty(data, key, value) {
            let _this = this
            Object.defineProperty(data, key, {
                enumerable: true,
                configurable: true,
                get() {
                    console.log("get...")
                    return value
                },
                set(newValue) {
                    // let event = new Event(key)
                    let event = new CustomEvent(key, {
                        detail: newValue
                    })
                    _this.dispatchEvent(event)
                    console.log("set...")
                    value = newValue;
                }
            });
        }
        compile() {
            let el = document.querySelector(this.$options.el)
            this.compileNode(el)
        }
        compileNode(el) {
            let childNodes = el.childNodes
            childNodes.forEach(node => {
                if (node.nodeType === 1) {
                    // 标签 
                    let attrs = node.attributes;
                    console.log(attrs);
                    [...attrs].forEach(attr => {
                        console.log(attr)
                        let attrName = attr.name
                        let attrValue = attr.value
                        if (attrName.indexOf('v-') === 0) {
                            attrName = attrName.substr(2)
                            if (attrName === "html") {
                                node.innerHTML = this.$options.data[attrValue]
                            } else if (attrName === "model") {
                                node.value = this.$options.data[attrValue]
                                node.addEventListener('input', e => {
                                    // console.log(e.target.value)
                                    //因为监听
                                    this.$options.data[attrValue] = e.target.value
                                })
                            }
                        }
                    });
                    if (node.childNodes.length > 0) {
                        this.compileNode(node)
                    }
                } else if (node.nodeType === 3) {
                    // 文本
                    let reg = /\{\{\s*(\S+)\s*\}\}/g
                    let textContent = node.textContent
                    if (reg.test(textContent)) {
                        console.log(RegExp.$1)
                        let $1 = RegExp.$1
                        // node.textContent = this.$options.data[$1]
                        node.textContent = node.textContent.replace(reg, this.$options.data[$1])
                        this.addEventListener($1, e => {
                            // 重新渲染
                            // console.log(this.$options.data[$1])
                            // console.log(e.detail)
                            let oldValue = this.$options.data[$1]
                            let reg = new RegExp(oldValue);
                            node.textContent = node.textContent.replace(reg, e.detail)
                        })
                    }
                }
            });
        }
    }
上一篇

正则表达式的学习与知识点归纳整理