初识:
实际上是通过Object.defineProperty()方法来实现的 talk is cheap, show your code
let obj = { } ;
Object. defineProperty ( obj, 'name' , { get ( ) { return document. querySelector ( '#name' ) . innerHTML; } , set ( newVal) { document. querySelector ( '#name' ) . innerHTML = val; }
} )
封装:
class Lz { constructor ( options) { this . $options = options; this . $data = options. data; this . observe ( this . $data) ; } observe ( value) { if ( ! value || typeof value !== 'object' ) { return ; } Object. keys ( value) . forEach ( key => { this . defineReactive ( value, key, value[ key] ) } ) } defineReactive ( obj, key, val) { Object. defineProperty ( obj, key, { get ( ) { return val} , set ( newVal) { if ( val === newVal) return ; console. log ( `数据更新辣: ${ val} --- > ${ newVal} ` ) ; val = newVal; } } ) }
}
引用Lz类
< body> < div id = " app" > </ div> < script src = " ./lz.js" > </ script> < script> const app = new Lz ( { el: '#app' , data: { foo: 'bar' , hello: { world: '您好,世界' } } } ) </ script>
</ body>
打开浏览器,在控制台输入app.$data.foo 尝试改变app.$data.foo的值 尝试改变app.$data.hello.world的值
浅拷贝
以上,对第一个属性foo的操作成功的触发了set函数. 由于浅拷贝复制的是引用,我们对对象的修改无法触发set. 简单的理解就是set只负责监听自己这一层的变化,下一层的变化.不予监听 解决办法,使用递归.给每一层添加一个setter方法.代码如下 只需在defineReactive里面对world对象设置set和get 重写defineReactive方法
defineReactive ( obj, key, val) { this . observe ( val) ; Object. defineProperty ( obj, key, { get ( ) { return val; } , set ( newVal) { if ( newVal === val) { return ; } console. log ( ` ${ key} 属性更新: ${ val} --- > ${ newVal} ` ) ; val = newVal; } } )
}