"use strict"; class OS { // } window.ObserveStorage = new OS();
{ : { : } :{ :{ :{ id : } } } }
class OS { constructor() { this.storage = new Map(); // this.listeners = new Map(); // this.serviceField = new Set([`on`, 'un']); //”” , .. . } }
let object = {key1: ”data”, key2: 1}
let wrapper = ObserveStorage.add(key, object); //return Proxy
let wrapper = ObserveStorage.add(object);
static __getId() { return (`${Math.random().toFixed(10).toString().replace("0.", "")}${Date.now()}`) }
add(...arg) { // 1 2 let key, object; if(arg.length == 1){ [object] = arg; key = OS.__getId(); // id } else [key, object] = arg; // , , , . // , . // 1) : if (this.storage.has(key)) { throw new Error(`key ${key} is already in use`); } // 2) () , , (: , , – ): let self = this; object.on = (...arg)=> self.on(key, ...arg); // object.un = (...arg)=> self.un(key, ...arg); // // storage const proxy = this.getProxy(key, object); //return Proxy // Map this.listeners.set(key, new Map()); //, , this.storage.set(key, proxy); // , , return proxy; }
// getProxy 2 , 1 – , , 2 – . getProxy(key, object){ let self = this; // , return new Proxy(object, { // get(target, field) { return target[field]; }, // set(target, field, value) { // , if(self.serviceField.has(field)) throw new Error(`Field ${field} is blocked for DB object`); const oldValue = target[field]; target[field] = value; // fire OS. self.fire(key, { type: oldValue ? "change" : "add", property: field, oldValue: oldValue, value: value, object: self.get(key) }); // , , Oo, - . return true }, // deleteProperty(target, field) { // if (!field in target || self.serviceField.has(field)) { return false; } const oldValue = target[field]; delete target[field]; self.fire(key, { type: "delete", property: field, oldValue: oldValue, value: undefined, object: self.get(key) }); return true; }, // Object.getOwnPropertyNames() , “” ownKeys(target) { let props = Object.keys(target) .filter(function (prop) { return !(self.serviceField.has(prop)); }); return props; } } ); }
self.fire(key, { type: oldValue ? "change" : "add", property: field, oldValue: oldValue, value: value, object: self.get(key) });
get(key) { if(this.storage.has(key)) return this.storage.get(key); else{ console.warn(`Element ${key} is not exist`); return undefined; } }
wrapper.on(callback, property = "*");
property = "*"
wrapper.on(event => console.log(JSON.stringify(event)), "value"); wrapper.data = "test"; // wrapper.value = 2; // Object{"type":"change","property":"value","oldValue":4,"value":2,"object":{"data":"test","value":2}} wrapper.on(event => console.log(JSON.stringify(event)), "*"); wrapper.data = "test"; // Object{"type":"change","property":"data","oldValue":”text”,"value":”test”,"object":{"data":"test","value":1}}
on(key, callback, property = "*") { // callback if (!key || !callback) { throw new Error("Key or callback is empty or not exist"); } // Map const listeners = this.listeners.get(key), // id subscriptionId = OS.__getId(); // , , , Map !listeners.has(property) && listeners.set(property, new Map()); // id listeners .get(property) .set(subscriptionId, callback); // id return subscriptionId; }
object.on = (...arg)=> self.on(key, ...arg);
wrapper.un(subscriptionId);
un(key, subscriptionId) { // , if (!key) { throw new Error("Key is empty or not exist"); } // const listeners = this.listeners.get(key); if (listeners) // Map for (let listener of listeners.values()) { // if (listener.delete(subscriptionId)) return true; } return false; }
fire(key, event) { // let listeners = this.listeners.get(key) ,property = event.property; // listeners.has(property) && this.fireListeners(event, listeners.get(property)); // listeners.has("*") && this.fireListeners(event, listeners.get("*")); }
fireListeners(event, listeners) { listeners.forEach((listener)=> { setTimeout(()=> listener(event), 0); }) }
Source: https://habr.com/ru/post/309978/
All Articles