js監聽資料的變化。
阿新 • • 發佈:2022-02-12
通過把屬性轉換為訪問器屬性,實現監聽。
物件屬性的更改通過設定 get, set。
陣列型別元素的更改通過在prototype過載操作資料的方法:slice、push、shift……
const OP = Object.prototype; const types = { obj: '[object Object]', array: '[object Array]' } const OAM = ['push', 'pop', 'shift', 'unshift', 'short', 'reverse', 'splice'] class Jsonob { constructor(obj, cb) {if (OP.toString.call(obj) !== types.obj && OP.toString.call(obj) !== types.array) { console.log('請傳入一個物件或陣列'); return false; } this._callback = cb; this.observe(obj); } observe(obj, path) { if (OP.toString.call(obj) === types.array) {this.overrideArrayProto(obj, path); } Object.keys(obj).forEach((key)=>{ let oldVal = obj[key]; let pathArray = path && path.slice(); if (pathArray) { pathArray.push(key); } else { pathArray = [key]; } Object.defineProperty(obj, key, { get:function() { return oldVal; }, set: (function(newVal) { if (oldVal !== newVal) { if (OP.toString.call(newVal) === '[object Object]') { this.observe(newVal, pathArray); } this._callback(newVal, oldVal, pathArray) oldVal = newVal } } ).bind(this) }) if (OP.toString.call(obj[key]) === types.obj || OP.toString.call(obj[key]) === types.array) { this.observe(obj[key], pathArray) } } , this) } overrideArrayProto(array, path) { // 儲存原始 Array 原型 var originalProto = Array.prototype, // 通過 Object.create 方法建立一個物件,該物件的原型是Array.prototype overrideProto = Object.create(Array.prototype), self = this, result; // 遍歷要重寫的陣列方法 OAM.forEach((method)=>{ Object.defineProperty(overrideProto, method, { value: function() { var oldVal = this.slice(); //呼叫原始原型上的方法 result = originalProto[method].apply(this, arguments); //繼續監聽新陣列 self.observe(this, path); self._callback(this, oldVal, path); return result; } }) } ); // 最後 讓該陣列例項的 __proto__ 屬性指向 假的原型 overrideProto array.__proto__ = overrideProto; } } var data = { a: 200, level1: { b: 'str', c: [1, 2, 3], level2: { d: 90 } } } var cb = (...val)=>{ console.log("calllback", ...val) } var rreess = new Jsonob(data,cb); data.level1.level2.d = 50 data.a = 998 data.level1.c.unshift(0)