0

I have an object of type ServiceWorkerRegistration that I want to stringify:

const reg = await navigator.serviceWorker.register(`https://www.myurl.com/Worker.js`, {scope:"/"});

When I console.log(reg) I get the following:

ServiceWorkerRegistration { installing: null, waiting: null, active: ServiceWorker, navigationPreload: NavigationPreloadManager, scope: "https://www.myurl.com/", updateViaCache: "imports", onupdatefound: null, pushManager: PushManager }
active: ServiceWorker { scriptURL: "https://www.myurl.com/Worker.js", state: "activated", onstatechange: null, … }
onerror: null
onstatechange: null
scriptURL: "https://www.myurl.com/Worker.js"
state: "activated"
<prototype>: ServiceWorkerPrototype { postMessage: postMessage(), scriptURL: Getter, state: Getter, … }
installing: null
navigationPreload: NavigationPreloadManager {  }
onupdatefound: null
pushManager: PushManager {  }
scope: "https://www.myurl.com/"
updateViaCache: "imports"
waiting: null
<prototype>: ServiceWorkerRegistrationPrototype { update: update(), unregister: unregister(), showNotification: showNotification(), … }

When I do typeof reg I get 'object'

However if I try to JSON.stringify(reg) I get {}. Likewise if I try and do Object.keys(reg) I get [].

I looked at answers such as Why does JSON.stringify return empty object notation "{}" for an object that seems to have properties? and Why does JSON.stringify on TypeError return an empty object which claims this happens when an object has no enumerable properties.

A band aid solution is that I manually print each field e.g. console.log(reg["installing"]);, or to manually reset each property as enumerable e.g. something like Object.defineProperty(reg, 'installing', {enumerable: true}) for each property.

However I would like to be able to do something like:

Object.keys(reg).forEach(key=>console.log(reg[key]));

And to do so, the object must have enumerable properties. How do I make the properties enumerable?

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Null Salad
  • 765
  • 2
  • 16
  • 31
  • 1
    You shouldn't modify the property settings of a built-in class. And built-in types like this generally don't make sense to convert to JSON, because they contain references to browser state that can't be serialized. – Barmar Dec 30 '22 at 22:15
  • 1
    Take a look at this https://stackoverflow.com/questions/30158515/list-all-prototype-properties-of-a-javascript-object – Konrad Dec 30 '22 at 22:17
  • 1
    Even if you manage to stringify it you won't get any of the methods back after reparsing. see: [How to restore original object/type from JSON?](https://stackoverflow.com/questions/14027168/how-to-restore-original-object-type-from-json) – pilchard Dec 30 '22 at 22:19
  • @Konrad that post was helpful, I can print out the values by going `Object.keys(Object.getPrototypeOf(reg)).forEach(key=>console.log(`key: ${key}, value: ${reg[key]}`))` – Null Salad Dec 30 '22 at 22:21

1 Answers1

0

From the comments my working solution is to just make a copy of the object (solution in TS):

const objCpy = (obj : any) : string =>{
    let newObj : any = {}
    const keys : string[] = Object.keys(Object.getPrototypeOf(obj));
    keys.forEach((key :  string)=>{
        let val : any = obj[key]
        if(typeof val === "object" && val !== null){
            val = objCpy(val)
        }
        newObj[key] = val;
    })
    return newObj;
}

then it stringifies as expected:

JSON.stringify(objCpy(reg))
>> `{"installing":{"scriptURL":"http://localhost:3001/Worker.js","state":"installing","onstatechange":null,"onerror":null},"waiting":null,"active":null,"navigationPreload":{},"scope":"http://localhost:3001/","updateViaCache":"imports","onupdatefound":null,"pushManager":{}}`
Null Salad
  • 765
  • 2
  • 16
  • 31