JS 实现对对象内参数的监听

使用过vue的人都知道vue拥有对参数监听的便捷方法,其原理就是使用数据劫持,在每次数据产生变化的时候触发其内在的函数,其书写方式如下

// 第一个参数填写欲监听的对象
// 第二个参数填写欲监听的对象下的属性
Object.defineProperty(obj, 'attr', {
	get() {
		return val
    },
	set(newValue) {
        console.log('触发!');
		val= newValue
    }
})

监听的功能通过上述的Object.defineProperty来实现,并借助了其第三个参数----属性描述符完成数据劫持,每次获取或修改数据,都将走过这一步,这也是vue的监听实现的底层逻辑。

如果开发者不设置这个高级选项,那么也就是进行一个默认的选项,getter默认返回当前对象的值,setter默认在修改值之后不进行任何操作。

下面的代码为具体的一个实例

let obj = {
        name: 'John',
        uid: '12345',
    }
    let _uid = obj.uid;

    Object.defineProperty(obj, 'uid', {
		get() {
			return _uid
        },
		set(newValue) {
            console.log('触发!');
			_uid = newValue
        }
    })
    // 测试代码
	setTimeout(() => {
		obj.uid = '54321'
		console.log(obj.uid);
	}, 3000)

在打开页面之后的三秒后,输出框会自动修改对象的值并触发setter内的事件。

其中值得注意的是上面的一个临时参数_uid,这个参数的作用就在用户获取uid的时候能够获取正确的数据。

因为一些内在原因,getter和setter无法调用自己所监听的对象属性。

javascript 对象监听更改 js监听对象属性值变化_vue.js


所以需要借助第三方的参数_uid来实现对象属性值的存储。

注意

这个监听的方法不仅可以实现对属性值的监听,也可以监听整个对象,如果对象内部的值产生变化就会回调setter。

let obj = {
        name: 'John',
        uid: '12345',
		details: {
            phoneNumber: '1234567890',
		}
    }
    let details = obj.details;

    Object.defineProperty(obj, 'details', {
		get() {
			return details
        },
		set(newValue) {
            console.log('触发!');
			details = newValue
        }
    })
	setTimeout(() => {
		// 修改了obj.details内部的手机号
		obj.details.phoneNumber= '12345678901'
		console.log(obj.details);
	}, 3000)

扩展

上述的方法虽然能监听对象内部的属性值的变化,如果只是监听外部的对象,是很难具体获取内部的哪个属性值发生了变化。这个还需后序的比较,非常麻烦,所以,JS原生还有一种方法可以批量地监听,这不仅能够监听对象内部的多个属性,也可以极大地减少代码量。

这个方法就是使用Object.defineProperty所衍生的Object.defineProperties

let obj = {
        name: 'John',
        uid: '12345',
        phoneNumber: '123456789',
    }
    let name = obj.name
    let uid = obj.uid
    let phoneNumber = obj.phoneNumber

    Object.defineProperties(obj, {
        'name': {
            get() {
                return name
            },
            set(value) {
                console.log('名字发生变化' + value)
                name = value
            }

        },
        'uid': {
            get() {
                return uid
            },
            set(value) {
                console.log('uid发生变化' + value)
                uid = value
            }
        },
        'phoneNumber': {
            get() {
                return phoneNumber
            },
            set(value) {
                console.log('phoneNumber发生变化' + value)
				phoneNumber = value
            }
        }
    })

    setTimeout(() => {
        obj.name = 'Tom'
	}, 1000)

	setTimeout(() => {
		obj.uid = '123456'
	}, 2000)
	
	setTimeout(() => {
		obj.phoneNumber = '123456789'
	}, 3000)

总结

好了,我们了解了监听的底层逻辑了,是时候自己模拟一遍vue的watch的方法了!!