初識:
實際上是通過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; } } )
}