前端面试常见问题-手写实现vue数据监听
面试最常问到的问题之一,前端框架vue的实现机制,很多同学都能侃侃而谈,vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。但是理解了不一定自己能实现,所以还是要手动实现才能理解更深刻。
话不多说,直接开始。首先定义一个要监听的对象
let dog = {name:'小花', age:'2'}
上面只是一个普通的js 对象,它的属性变化 可以直接使用 dog.name = ** dog.age=** 来改变,我们现在使用 Object.defineProperty() 来实现对对象的监听。
let dog = {}
let ageNum = '2'
Object.defineProperty(dog,'age',{
enumerable:true,
configurable:true,
get(){
console.log('age属性被读取了')
return ageNum
}
set(newVal){
console.log('age属性被修改了')
return newVal
}
})
通过defineProperty 给 dog 对象定义了一个 属性age,并把这个属性定义了get 和 set方法监听,当该属性被读取或者修改的时候触发get()和 set() 方法。但是一个对象会有很多个属性,我们现在只能监测到 age属性,要一个一个去监听显然不现实,而且还会有很多新增属性,所以我们使用 递归的方式来实现所有属性的监听。
/**
* 定义Observer类通过递归的方式把一个对象的所有属性都转化成可观测对象
*/
export default Observer{
constructor(value){
this.value = value
// 表示此对象已被设置监听
def(value,'_observered',this)
if(Array.isArray(value)){
//如果是数组 特殊处理
}else{
// 执行监听方法
this.listen(value)
}
}
listen(obj:Object){
const keys = Object.keys(obj)
// 遍历把属性设置监听
for(let i in keys){
defineAttr(obj, keys[i])
}
}
}
// 给对象设置监听方法
function defineAttr(obj,key,value){
// 判断 如果参数只有两个 则返回原始值
if(arguments.length===2){
value = arguments[key]
}
if(typeof value === 'object'){
new Observer(value)
}
Object.defineProperty(obj,key,{
enumerable:true,
configurable:true,
get(){
console.log(`${key}属性被读取了`)
return value
}
set(newVal){
console.log(`${key}属性被修改了`)
return newVal
}
})
}
在以上代码中,我们定义一个Observer类,用它使普通对象变为可检测对象,把已经设置为响应式的对象设置一个 _observered 属性表示已经被监听,避免重复操作,当对象的属性还是对象时使用 new Observer(value) 递归来检测子对象,这样子所有的对象和子属性对象都成为了 setter和 gerrer模式了。现在我们这样定义对象即可实现属性监测。
let dog = new Observer({
name:'小花',
age:'2'
})