Object definProperty基本使用 及 简单实现 Vue 的双向绑定原理及属性代理

888 阅读2分钟

Object.definedProperty 可以给对象添加一个属性,或者修改一个已有的属性,添加一些配置项

参数

const obj = {}

/**
* obj  源对象
* prop  要定义或修改的属性的名称或 Symbol 。
* descriptor  要定义或修改的属性描述符。
*/
Object.defineProperty(obj, 'a', {
    value: 1,  // 值
    configurable: true,  // 当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。 默认为 false。
    writable: true,  // 是否可写。默认为 false
    enumerable: true, // 当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。默认为 false
    get() {}, // 获取值时触发
    set() {} // 设置值时触发
})

实现对一个对象的监听,在访问属性、设置属性时,打印出 'get xx attr' 'set xx attr'

var object = {
    a: 1,
    b: 2
}

for(let key in object) {
    let value = obj[key]

    Object.defineProperty(object, key, {
        configurable: true,
        enumerable: true,
        get() {
            console.log(`get ${key} attr`)
            return value
        },
        set(newValue) { 
            console.log(`set ${key} attr`)
            value = newValue
        }
    })
}

object.a    // get a attr
object.b = 3    // ser a atter 设置值的时候并不会触发 get 方法

设置值的时候并不会触发 get 方法

实现类似 Vue 的响应式代理

如访问 this.a 实际访问的是 this.$data.a

var Vue = {
    $data: {
        a: 1,
        b: 2
    }
}

for(let key in Vue.$data) {
    let value = Vue.$data[key]

    Object.defineProperty(Vue, key, {
        configurable: true,
        enumerable: true,
        get() {
            return Vue.$data[key]
        },
        set(newValue) { 
            Vue.$data[key] = newValue
        }
    })
}

console.log(Vue.a, Vue.$data.a) // 1, 1
Vue.a = 2
console.log(Vue.a, Vue.$data.a) // 2, 2
Vue.$data.b = 3
console.log(Vue.b, Vue.$data.b) // 3, 3

简单实现 Vue 中对数组的劫持处理

var arr = [1, {a: 1}, 3]

for (var i = 0; i < arr.length; i++) {
    if (typeof arr[i] === 'object' && arr[i] !== null) {
        for (let key in arr[i]) {
            let value = arr[i][key]

            Object.defineProperty(arr[i], key, {
                configurable: true,
                enumerable: true,
                get() {
                    console.log(`get ${key} attr`)
                    return value
                },
                set(newValue) {
                    console.log(`set ${key} attr`)
                    value = newValue
                }
            })
        }
    }
}

// arrPro 一个桥梁拦截了原生的 push 方法,然后在调用原生的 push 
// 拦截是因为防止原型链污染
var arrPro = Object.create(Array.prototype)
arr.__proto__ = arrPro
const push = arrPro.push

arrPro.push = function(argu) {
    if (typeof argu === 'object' && argu !== null) {
        for (let key in argu) {
            let value = argu[key]

            Object.defineProperty(argu, key, {
                configurable: true,
                enumerable: true,
                get() {
                    console.log(`get ${key} attr`)
                    return value
                },
                set(newValue) {
                    console.log(`set ${key} attr`)
                    value = newValue
                }
            })
        }
    }

    push.call(this, argu)
}

arr[0] = 0
arr[1].a = 'a'
// 使得添加的数据成响应式
arr.push({b: 2})

【笔记不易,如对您有帮助,请点赞,谢谢】