diff --git a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js index 79874cd6d4ee3a711ccdc36bc621ccf61d786a23..078f7083e72759d366f624dd1d5660aef9ef2dee 100644 --- a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js +++ b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js @@ -81,6 +81,15 @@ * @since 9 */ class LocalStorage extends NativeLocalStorage { + /* + get access to provded LocalStorage instance thru Stake model + @StageModelOnly + @form + @since 10 + */ + static getShared() { + return LocalStorage.GetShared(); + } /** * Construct new instance of LocalStorage * initialzie with all properties and their values that Object.keys(params) returns @@ -97,15 +106,6 @@ class LocalStorage extends NativeLocalStorage { this.initializeProps(initializingProperties); } } - /* - get access to provded LocalStorage instance thru Stake model - @StageModelOnly - @form - @since 10 - */ - static getShared() { - return LocalStorage.GetShared(); - } /** * clear storage and init with given properties * @param initializingProperties @@ -494,13 +494,6 @@ class LocalStorage extends NativeLocalStorage { * @since 7 */ class AppStorage extends LocalStorage { - /** singleton class, app can not create instances - * - * not a public / sdk function - */ - constructor(initializingProperties) { - super(initializingProperties); - } /** * create and initialize singleton * initialzie with all properties and their values that Object.keys(params) returns @@ -911,6 +904,13 @@ class AppStorage extends LocalStorage { } return AppStorage.instance_; } + /** singleton class, app can not create instances + * + * not a public / sdk function + */ + constructor(initializingProperties) { + super(initializingProperties); + } } // instance functions below: // Should all be protected, but TS lang does not allow access from static member to protected member @@ -934,16 +934,6 @@ AppStorage.instance_ = undefined; * public API to manage IPropertySubscriber */ class SubscriberManager { - /** - * SubscriberManager is a singleton created by the framework - * do not use - * - * internal method - */ - constructor() { - this.subscriberById_ = new Map(); - - } /** * check subscriber is known * same as ES6 Map.prototype.has() @@ -1121,6 +1111,16 @@ class SubscriberManager { makeId() { return ViewStackProcessor.MakeUniqueId(); } + /** + * SubscriberManager is a singleton created by the framework + * do not use + * + * internal method + */ + constructor() { + this.subscriberById_ = new Map(); + + } } /* * Copyright (c) 2022 Huawei Device Co., Ltd. @@ -1347,14 +1347,6 @@ class SubscribaleAbstract extends SubscribableAbstract { * since 9 */ class PersistentStorage { - /** - * all following methods are framework internal - */ - constructor() { - this.links_ = new Map(); - this.id_ = SubscriberManager.MakeId(); - SubscriberManager.Add(this); - } /** * * @param storage method to be used by the framework to set the backend @@ -1494,6 +1486,14 @@ class PersistentStorage { PersistentStorage.storage_.set(propName, PersistentStorage.getOrCreate().links_.get(propName).get()); } + /** + * all following methods are framework internal + */ + constructor() { + this.links_ = new Map(); + this.id_ = SubscriberManager.MakeId(); + SubscriberManager.Add(this); + } keys() { return this.links_.keys(); } @@ -1604,10 +1604,6 @@ PersistentStorage.instance_ = undefined; * */ class Environment { - constructor() { - this.props_ = new Map(); - Environment.envBackend_.onValueChanged(this.onValueChanged.bind(this)); - } static getOrCreate() { if (Environment.instance_) { // already initialized @@ -1670,6 +1666,10 @@ class Environment { static Keys() { return Environment.getOrCreate().keys(); } + constructor() { + this.props_ = new Map(); + Environment.envBackend_.onValueChanged(this.onValueChanged.bind(this)); + } envProp(key, value) { let prop = AppStorage.prop(key); if (prop) { @@ -2185,23 +2185,6 @@ class ExtendableProxy { } } class ObservedObject extends ExtendableProxy { - /** - * To create a new ObservableObject use CreateNew function - * - * constructor create a new ObservableObject and subscribe its owner to propertyHasChanged - * notifications - * @param obj raw Object, if obj is a ObservableOject throws an error - * @param objectOwner - */ - constructor(obj, handler, objectOwningProperty) { - super(obj, handler); - if (ObservedObject.IsObservedObject(obj)) { - stateMgmtConsole.error("ObservableOject constructor: INTERNAL ERROR: after jsObj is observedObject already"); - } - if (objectOwningProperty != undefined) { - this[SubscribableHandler.SUBSCRIBE] = objectOwningProperty; - } - } // end of constructor /** * Factory function for ObservedObjects / * wrapping of objects for proxying @@ -2367,6 +2350,23 @@ class ObservedObject extends ExtendableProxy { ? Object.getPrototypeOf(proto.constructor.prototype) : proto; } + /** + * To create a new ObservableObject use CreateNew function + * + * constructor create a new ObservableObject and subscribe its owner to propertyHasChanged + * notifications + * @param obj raw Object, if obj is a ObservableOject throws an error + * @param objectOwner + */ + constructor(obj, handler, objectOwningProperty) { + super(obj, handler); + if (ObservedObject.IsObservedObject(obj)) { + stateMgmtConsole.error("ObservableOject constructor: INTERNAL ERROR: after jsObj is observedObject already"); + } + if (objectOwningProperty != undefined) { + this[SubscribableHandler.SUBSCRIBE] = objectOwningProperty; + } + } // end of constructor } ObservedObject.__IS_OBSERVED_OBJECT = Symbol("_____is_observed_object__"); ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT = Symbol("_____raw_object__"); @@ -3154,6 +3154,23 @@ class SynchedPropertyNesedObject extends ObservedPropertyObjectAbstract { // implemented in C++ for release // and in utest/view_native_mock.ts for testing class View extends NativeViewFullUpdate { + get localStorage_() { + if (!this.localStoragebackStore_) { + + this.localStoragebackStore_ = new LocalStorage({ /* emty */}); + } + return this.localStoragebackStore_; + } + set localStorage_(instance) { + if (!instance) { + // setting to undefined not allowed + return; + } + if (this.localStoragebackStore_) { + stateMgmtConsole.error(`${this.constructor.name} is setting LocalStorage instance twice`); + } + this.localStoragebackStore_ = instance; + } /** * Create a View * @@ -3197,23 +3214,6 @@ class View extends NativeViewFullUpdate { SubscriberManager.Add(this); } - get localStorage_() { - if (!this.localStoragebackStore_) { - - this.localStoragebackStore_ = new LocalStorage({ /* emty */}); - } - return this.localStoragebackStore_; - } - set localStorage_(instance) { - if (!instance) { - // setting to undefined not allowed - return; - } - if (this.localStoragebackStore_) { - stateMgmtConsole.error(`${this.constructor.name} is setting LocalStorage instance twice`); - } - this.localStoragebackStore_ = instance; - } // globally unique id, this is different from compilerAssignedUniqueChildId! id__() { return this.id_; @@ -3866,11 +3866,11 @@ class SynchedPropertyObjectOneWayPU extends ObservedPropertyObjectAbstractPU { this.source_.addSubscriber(this); } else { - // code path for + // code path for // 1- source is of same type C in parent, source is its value, not the backing store ObservedPropertyObject // 2- nested Object/Array inside observed another object/array in parent, source is its value if (!((source instanceof SubscribableAbstract) || ObservedObject.IsObservedObject(source))) { - stateMgmtConsole.warn(`@Prop ${this.info()} Provided source object's class + stateMgmtConsole.warn(`@Prop ${this.info()} Provided source object's class lacks @Observed class decorator. Object property changes will not be observed.`); } @@ -3948,7 +3948,7 @@ class SynchedPropertyObjectOneWayPU extends ObservedPropertyObjectAbstractPU { } if (!ObservedObject.IsObservedObject(newValue)) { - stateMgmtConsole.warn(`@Prop ${this.info()} Set: Provided new object's class + stateMgmtConsole.warn(`@Prop ${this.info()} Set: Provided new object's class lacks '@Observed' class decorator. Object property changes will not be observed.`); } if (this.resetLocalValue(newValue, /* needCopyObject */ false)) { @@ -3977,23 +3977,23 @@ class SynchedPropertyObjectOneWayPU extends ObservedPropertyObjectAbstractPU { // note: We can not test for newObservedObjectValue == this.localCopyObservedObject_ // here because the object might still be the same, but some property of it has changed if (typeof newObservedObjectValue !== "object") { - // if not undefined or null, then the provided newObservedObjectValue must be an Object + // if not undefined or null, then the provided newObservedObjectValue must be an Object stateMgmtConsole.error(`SynchedPropertyOneWayObjectPU[${this.id__()}]: setLocalValue new value must be an Object.`); } - // unsubscribe from old local copy + // unsubscribe from old local copy if (this.localCopyObservedObject_ instanceof SubscribableAbstract) { this.localCopyObservedObject_.removeOwningProperty(this); } else { ObservedObject.removeOwningProperty(this.localCopyObservedObject_, this); } - // shallow/deep copy value + // shallow/deep copy value // needed whenever newObservedObjectValue comes from source // not needed on a local set (aka when called from set() method) let copy = needCopyObject ? this.copyObject(newObservedObjectValue, this.info_) : newObservedObjectValue; if (copy instanceof SubscribableAbstract) { this.localCopyObservedObject_ = copy; - // deep copy will copy Set of subscribers as well. But local copy only has its own subscribers + // deep copy will copy Set of subscribers as well. But local copy only has its own subscribers // not those of its parent value. this.localCopyObservedObject_.clearOwningProperties(); this.localCopyObservedObject_.addOwningProperty(this); @@ -4012,7 +4012,7 @@ class SynchedPropertyObjectOneWayPU extends ObservedPropertyObjectAbstractPU { return true; } copyObject(value, propName) { - // ViewStackProcessor.getApiVersion function is not present in API9 + // ViewStackProcessor.getApiVersion function is not present in API9 // therefore shallowCopyObject will always be used in API version 9 and before // but the code in this file is the same regardless of API version @@ -4125,13 +4125,26 @@ class SynchedPropertyObjectOneWayPU extends ObservedPropertyObjectAbstractPU { } else if (obj instanceof Object) { copy = Array.isArray(obj) ? [] : {}; - Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); - for (const objKey of Object.keys(obj)) { + const proto = Object.getPrototypeOf(obj); + Object.setPrototypeOf(copy, proto); + let objKeys = proto ? Object.getOwnPropertyNames(proto) : []; + objKeys = objKeys.concat(Object.getOwnPropertyNames(obj)); + for (const objKey of objKeys) { stack.push({ name: objKey }); copiedObjects.set(obj, copy); - Reflect.set(copy, objKey, getDeepCopyOfObjectRecursive(obj[objKey])); + if (obj[objKey] instanceof Function) { + Object.defineProperty(copy, objKey, { + value: () => { return obj[objKey](); } + }); + } + else { + Reflect.set(copy, objKey, getDeepCopyOfObjectRecursive(obj[objKey])); + } stack.pop(); } + } + else { + } return ObservedObject.IsObservedObject(obj) ? ObservedObject.createNew(copy, null) : copy; } @@ -4560,6 +4573,27 @@ const UndefinedElmtId = -1; // implemented in C++ for release // and in utest/view_native_mock.ts for testing class ViewPU extends NativeViewPartialUpdate { + get localStorage_() { + if (!this.localStoragebackStore_ && this.parent_) { + + this.localStoragebackStore_ = this.parent_.localStorage_; + } + if (!this.localStoragebackStore_) { + + this.localStoragebackStore_ = new LocalStorage({ /* empty */}); + } + return this.localStoragebackStore_; + } + set localStorage_(instance) { + if (!instance) { + // setting to undefined not allowed + return; + } + if (this.localStoragebackStore_) { + stateMgmtConsole.error(`${this.constructor.name} is setting LocalStorage instance twice`); + } + this.localStoragebackStore_ = instance; + } /** * Create a View * @@ -4619,27 +4653,6 @@ class ViewPU extends NativeViewPartialUpdate { SubscriberManager.Add(this); } - get localStorage_() { - if (!this.localStoragebackStore_ && this.parent_) { - - this.localStoragebackStore_ = this.parent_.localStorage_; - } - if (!this.localStoragebackStore_) { - - this.localStoragebackStore_ = new LocalStorage({ /* empty */}); - } - return this.localStoragebackStore_; - } - set localStorage_(instance) { - if (!instance) { - // setting to undefined not allowed - return; - } - if (this.localStoragebackStore_) { - stateMgmtConsole.error(`${this.constructor.name} is setting LocalStorage instance twice`); - } - this.localStoragebackStore_ = instance; - } // globally unique id, this is different from compilerAssignedUniqueChildId! id__() { return this.id_; diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_synced_property_object_one_way.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_synced_property_object_one_way.ts index f78727c72f7fb46f8e6c2ca6b837e2a92cb6d7b5..e669d6b0defe170f9c582be9e1fa1a44ada03e75 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_synced_property_object_one_way.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_synced_property_object_one_way.ts @@ -16,46 +16,46 @@ /** * SynchedPropertyObjectOneWayPU * implementation of @Prop decorated variables of type class object - * + * * all definitions in this file are framework internal - * + * */ /** * Initialisation scenarios: * ------------------------- - * + * * 1 - no local initialization, source provided (its ObservedObject value) * wrap the ObservedObject into an ObservedPropertyObjectPU * deep copy the ObservedObject into localCopyObservedObject_ - * + * * 2 - local initialization, no source provided * app transpiled code calls set * leave source_ undefined - * no deep copy needed, but provided local init might need wrapping inside an ObservedObject to set to + * no deep copy needed, but provided local init might need wrapping inside an ObservedObject to set to * localCopyObservedObject_ - * + * * 3 local initialization, source provided (its ObservedObject value) * current app transpiled code is not optional * sets source in constructor, as in case 1 * calls set() to set the source value, but this will not deepcopy - * + * * Update scenarios: * ----------------- - * + * * 1- assignment of a new Object value: this.aProp = new ClassA() * rhs can be ObservedObject because of @Observed decoration or now * notifyPropertryHasChangedPU - * + * * 2- local ObservedObject member property change * objectPropertyHasChangedPU called, eventSource is the ObservedObject stored in localCopyObservedObject_ * no need to copy, notifyPropertryHasChangedPU - * + * * 3- Rerender of the custom component triggered from the parent * reset() is called (code generated by the transpiler), set the value of source_ , if that causes a change will call syncPeerHasChanged * syncPeerHasChanged need to deep copy the ObservedObject from source to localCopyObservedObject_ * notifyPropertryHasChangedPU - * + * * 4- source_ ObservedObject member property change * objectPropertyHasChangedPU called, eventSource is the ObservedObject stored source_.getUnmonitored * notifyPropertryHasChangedPU @@ -71,9 +71,9 @@ class SynchedPropertyObjectOneWayPU // reference to the source variable in parent component private source_: ObservedPropertyAbstract; - // true for @Prop code path, + // true for @Prop code path, // false for @(Local)StorageProp - private sourceIsOwnObject : boolean; + private sourceIsOwnObject: boolean; constructor(source: ObservedPropertyAbstract | C, owningChildView: IPropertySubscriber, @@ -88,11 +88,11 @@ class SynchedPropertyObjectOneWayPU // subscribe to receive value change updates from LocalStorage source property this.source_.addSubscriber(this); } else { - // code path for + // code path for // 1- source is of same type C in parent, source is its value, not the backing store ObservedPropertyObject // 2- nested Object/Array inside observed another object/array in parent, source is its value if (!((source instanceof SubscribableAbstract) || ObservedObject.IsObservedObject(source))) { - stateMgmtConsole.warn(`@Prop ${this.info()} Provided source object's class + stateMgmtConsole.warn(`@Prop ${this.info()} Provided source object's class lacks @Observed class decorator. Object property changes will not be observed.`); } stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: constructor @Prop wrapping source in a new ObservedPropertyObjectPU`); @@ -113,17 +113,17 @@ class SynchedPropertyObjectOneWayPU aboutToBeDeleted() { if (this.source_) { this.source_.removeSubscriber(this); - if (this.sourceIsOwnObject == true && this.source_.numberOfSubscrbers()==0){ + if (this.sourceIsOwnObject == true && this.source_.numberOfSubscrbers() == 0) { stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: aboutToBeDeleted. owning source_ ObservedPropertySimplePU, calling its aboutToBeDeleted`); this.source_.aboutToBeDeleted(); - } + } this.source_ = undefined; } super.aboutToBeDeleted(); } - private getSourceObservedPropertyFakeName() : string { + private getSourceObservedPropertyFakeName(): string { return `${this.info()}_source`; } @@ -145,15 +145,15 @@ class SynchedPropertyObjectOneWayPU /** * event emited by wrapped ObservedObject, when one of its property values changes - * @param souceObject - * @param changedPropertyName + * @param souceObject + * @param changedPropertyName */ public objectPropertyHasChangedPU(sourceObject: ObservedObject, changedPropertyName: string) { - stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: objectPropertyHasChangedPU '${changedPropertyName}' has changed.`); - this.notifyPropertyHasChangedPU(); + stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: objectPropertyHasChangedPU '${changedPropertyName}' has changed.`); + this.notifyPropertyHasChangedPU(); } - public objectPropertyHasBeenReadPU(sourceObject: ObservedObject, changedPropertyName : string) { + public objectPropertyHasBeenReadPU(sourceObject: ObservedObject, changedPropertyName: string) { stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: \ objectPropertyHasBeenReadPU: contained ObservedObject property '${changedPropertyName}' has been read.`); this.notifyPropertyHasBeenReadPU(); @@ -181,7 +181,7 @@ class SynchedPropertyObjectOneWayPU stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: set to newV value.`); if (!ObservedObject.IsObservedObject(newValue)) { - stateMgmtConsole.warn(`@Prop ${this.info()} Set: Provided new object's class + stateMgmtConsole.warn(`@Prop ${this.info()} Set: Provided new object's class lacks '@Observed' class decorator. Object property changes will not be observed.`); } @@ -200,7 +200,7 @@ class SynchedPropertyObjectOneWayPU stateMgmtConsole.error(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: reset @Prop --- No source_. Internal error!`); } } - + /* unsubscribe from previous wrapped ObjectObject take a shallow or (TODO) deep copy @@ -208,48 +208,48 @@ class SynchedPropertyObjectOneWayPU Therefore, conditionally wrap the object, then subscribe return value true only if localCopyObservedObject_ has been changed */ - private resetLocalValue(newObservedObjectValue: C, needCopyObject : boolean): boolean { - // note: We can not test for newObservedObjectValue == this.localCopyObservedObject_ - // here because the object might still be the same, but some property of it has changed - - if (typeof newObservedObjectValue !== "object") { - // if not undefined or null, then the provided newObservedObjectValue must be an Object - stateMgmtConsole.error(`SynchedPropertyOneWayObjectPU[${this.id__()}]: setLocalValue new value must be an Object.`) - } - - // unsubscribe from old local copy - if (this.localCopyObservedObject_ instanceof SubscribableAbstract) { - (this.localCopyObservedObject_ as SubscribableAbstract).removeOwningProperty(this); - } else { - ObservedObject.removeOwningProperty(this.localCopyObservedObject_, this); - } + private resetLocalValue(newObservedObjectValue: C, needCopyObject: boolean): boolean { + // note: We can not test for newObservedObjectValue == this.localCopyObservedObject_ + // here because the object might still be the same, but some property of it has changed - // shallow/deep copy value - // needed whenever newObservedObjectValue comes from source - // not needed on a local set (aka when called from set() method) - let copy = needCopyObject ? this.copyObject(newObservedObjectValue, this.info_) : newObservedObjectValue; + if (typeof newObservedObjectValue !== "object") { + // if not undefined or null, then the provided newObservedObjectValue must be an Object + stateMgmtConsole.error(`SynchedPropertyOneWayObjectPU[${this.id__()}]: setLocalValue new value must be an Object.`) + } - if (copy instanceof SubscribableAbstract) { - this.localCopyObservedObject_ = copy; - // deep copy will copy Set of subscribers as well. But local copy only has its own subscribers - // not those of its parent value. - (this.localCopyObservedObject_ as unknown as SubscribableAbstract).clearOwningProperties(); - (this.localCopyObservedObject_ as unknown as SubscribableAbstract).addOwningProperty(this); - } else if (ObservedObject.IsObservedObject(copy)) { - // case: new ObservedObject - this.localCopyObservedObject_ = copy; - ObservedObject.addOwningProperty(this.localCopyObservedObject_, this); - } else { - // wrap newObservedObjectValue raw object as ObservedObject and subscribe to it - stateMgmtConsole.warn(`@Prop ${this.info()} Provided source object's class \ + // unsubscribe from old local copy + if (this.localCopyObservedObject_ instanceof SubscribableAbstract) { + (this.localCopyObservedObject_ as SubscribableAbstract).removeOwningProperty(this); + } else { + ObservedObject.removeOwningProperty(this.localCopyObservedObject_, this); + } + + // shallow/deep copy value + // needed whenever newObservedObjectValue comes from source + // not needed on a local set (aka when called from set() method) + let copy = needCopyObject ? this.copyObject(newObservedObjectValue, this.info_) : newObservedObjectValue; + + if (copy instanceof SubscribableAbstract) { + this.localCopyObservedObject_ = copy; + // deep copy will copy Set of subscribers as well. But local copy only has its own subscribers + // not those of its parent value. + (this.localCopyObservedObject_ as unknown as SubscribableAbstract).clearOwningProperties(); + (this.localCopyObservedObject_ as unknown as SubscribableAbstract).addOwningProperty(this); + } else if (ObservedObject.IsObservedObject(copy)) { + // case: new ObservedObject + this.localCopyObservedObject_ = copy; + ObservedObject.addOwningProperty(this.localCopyObservedObject_, this); + } else { + // wrap newObservedObjectValue raw object as ObservedObject and subscribe to it + stateMgmtConsole.warn(`@Prop ${this.info()} Provided source object's class \ lacks @Observed class decorator. Object property changes will not be observed.`); - this.localCopyObservedObject_ = ObservedObject.createNew(copy, this); - } - return true; + this.localCopyObservedObject_ = ObservedObject.createNew(copy, this); + } + return true; } private copyObject(value: C, propName: string): C { - // ViewStackProcessor.getApiVersion function is not present in API9 + // ViewStackProcessor.getApiVersion function is not present in API9 // therefore shallowCopyObject will always be used in API version 9 and before // but the code in this file is the same regardless of API version stateMgmtConsole.debug(`targetApiVersion: \ @@ -263,7 +263,7 @@ class SynchedPropertyObjectOneWayPU } // API 9 code path - private shallowCopyObject(value: C, propName: string) : C { + private shallowCopyObject(value: C, propName: string): C { let rawValue = ObservedObject.GetRawObject(value); let copy: C; @@ -331,7 +331,7 @@ class SynchedPropertyObjectOneWayPU let copiedObjects = new Map(); return getDeepCopyOfObjectRecursive(obj); - + function getDeepCopyOfObjectRecursive(obj: any): any { if (!obj || typeof obj !== 'object') { return obj; @@ -367,13 +367,24 @@ class SynchedPropertyObjectOneWayPU copy.setTime(obj.getTime()); } else if (obj instanceof Object) { copy = Array.isArray(obj) ? [] : {}; - Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); - for (const objKey of Object.keys(obj)) { + const proto = Object.getPrototypeOf(obj); + Object.setPrototypeOf(copy, proto); + let objKeys = proto ? Object.getOwnPropertyNames(proto) : []; + objKeys = objKeys.concat(Object.getOwnPropertyNames(obj)); + for (const objKey of objKeys) { stack.push({ name: objKey }); copiedObjects.set(obj, copy); - Reflect.set(copy, objKey, getDeepCopyOfObjectRecursive(obj[objKey])); + if (obj[objKey] instanceof Function) { + Object.defineProperty(copy, objKey, { + value: () => { return obj[objKey](); } + }); + } else { + Reflect.set(copy, objKey, getDeepCopyOfObjectRecursive(obj[objKey])); + } stack.pop(); } + } else { + stateMgmtConsole.debug(`@Prop deepCopyObject: object does not match any of the specified instances`); } return ObservedObject.IsObservedObject(obj) ? ObservedObject.createNew(copy, null) : copy; }