How do you make an JavaScript object's properties enumerable? - javascript

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?

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":{}}`

Related

How can I use optional chaining in object data when I use JavaScript?

I'm taking singlePost data from Redux and making it into an array using Object.keys.
However, when rendering is in progress, singlePost is received late, so when I try to console.log, null is recorded at first and then the correct data comes in.
So this error is printed.
Cannot convert undefined or null to object
this is my code
const Explain = ({navigation, route}) => {
const {singlePost} = useSelector((state) => state.post);
console.log("singlePost:",singlePost);
// singlePost: null first recorded
// singlePost:
// singlePost = {
// User: {
// id: 3,
// nickname: "bill",
// },
// content1: "number1",
// content2: "number2", second recorded
// content3: "bye",
// content4: "empty",
// content5: "empty",
// content6: "empty",
// content7: "empty",
// content8: "number3",
// content9: "empty",
// content10: "empty",
// };
const contentOnly = Object.keys(singlePost)
return (
);
};
export default Explain;
How can I fix this error? Can I use optional chaining in object?
How can I fix my code?
There's no need for optional chaining. Optional chaining is only useful when accessing a property of a possibly-null object, and you're calling a function with the object as an argument.
You might be thinking of the nullish coalescing operator, which gives you a different value if the one under inspection is null or undefined, which you could possibly use here via:
const contentOnly = Object.keys(singlePost ?? {});
However, I'd say that's a rather roundabout way of getting an empty array. I'd just use a ternary, or conditional operator:
const contentOnly = singlePost == null ? [] : Object.keys(singlePost);
This tells readers exactly what they're going to get, especially if you set singlePost to null by default.
you can check the typeof the variable and whether it's null or not as below:
const contentOnly = (typeof singlePost === 'object' && singlePost !== null) ? Object.keys(singlePost): []
In your case you can check if singlePost is defined:
const contentOnly = Object.keys(singlePost || {})
// or
if(singlePost && typeof singlePost == 'object') {...do something}
If your need optional chaining(according to question title). You can do something like this
singlePost && singlePost.content1
// or with new syntax
singlePost?.content1
Pay attention - the new syntax is not supported by all environments

Shallow copy JavaScript object without references

How can I shallowly copy an JavaScript object and get rid of all non-primitive values (all references), while keeping all properties of the given object. Values of the properties might turn to null in this process.
Object.assign, lodash clone and the spread operator allow us to get a shallow copy of an object. However, despite the naming, the result object is not shallow [fact]. It has copied all references too, so the whole object tree is still accessible.
For a analytics solution, I need to get rid of anything deeper than one level.
How can I do it (libraries are also ok, ES6 is fine) without writing dozens of rules to deal with all the possible data types? Ideally, objects and array properties are not lost, but replaced with something, e.g. null or empty objects/arrays.
Example
const source = {
nr: 1,
str: 'ok',
obj: {
uhOh: 'kill me'
},
arr: ['well ok', { uhOh: 'uhOh' }],
}
// apply voodoo
const expected = {
nr: 1,
str: 'ok',
obj: {},
arr: [],
}
// This would also be an valid result:
const expected = {
nr: 1,
str: 'ok',
obj: null,
arr: null,
}
You could loop through the keys of the object using for...in. If the value is an object, set the key to null in expected, else set the value in expected to the value from source
const source = {
nr: 1,
str: 'ok',
obj: {
uhOh: 'kill me'
},
arr: ['well ok', {
uhOh: 'uhOh'
}],
}
const expected = {};
for (const key in source) {
if (typeof source[key] === 'object')
expected[key] = null
else
expected[key] = source[key]
}
console.log(expected)
This is not an answer in it's own right, but an addendum to the excellent answer by #adiga, this time using typescript and a type parameter:
private primitiveClone<T>(source: T): T {
const dto = Object.assign({}, source);
for (const key in dto) {
if (typeof dto[key] === 'object') {
dto[key] = null;
}
}
return dto;
}
usage
var simpleClone = primitiveClone(data);

Assign, and set js object property conditionally [duplicate]

This question already has answers here:
In JavaScript, how to conditionally add a member to an object?
(29 answers)
Conditionally set an object property
(7 answers)
How to conditionally add properties to a javascript object literal
(8 answers)
Closed 8 months ago.
Here's my code:
app.post('/ujfeladat', (req, res) => {
const {nev, tipus, szid} = req.body;
const hianyos = () => {
if(tipus === 'hianyos'){
return {rang: -1}
}
return
}
db('fl').insert({
nev: nev,
tipus: tipus,
szid: szid,
hianyos() //returns an error
}).returning('*')
.then(data => res.json(data))
.catch(err => console.log(err))
})
How can i do that to add the rang property to the object only if the tipus === 'hianyos'?
Updated answer:
Here's how you can do it:
// Will result in { foo: 'foo', bar: 'bar'}
const item = {
foo: 'foo',
... true && { bar: 'bar' },
... false && { falsy: 'falsy' },
}
console.log(item)
Explanations:
Short-circuit evaluation (true && {}, false && {}) would return an Object or a Boolean false value.
In the case an Object is returned, its properties get spread and assigned to the parent object.
In the case false value is returned, the parent object isn't polluted, because ES6 treats false, undefined, null and etc values as {}. Therefore spreading ...{} won't assign any properties to the parent object. More details about this, you can find here.
Original answer:
Here's how you can do it:
db('fl').insert({
nev: nev,
tipus: tipus,
szid: szid,
...tipus === 'hianyos' ? { rang: -1 } : {}
})
Explanations:
As you can see the ternary operator always returns an object.
If the condition is true, then it returns { rang: -1 }, otherwise an empty object {}.
After that we spread out ... the resulted object (from the ternary operation) and the object's properties are assigned to the parent object.
If there aren't any properties, then nothing will be assigned, which is our goal.
Code example: (sometimes few lines of code better than a thousands of words)
// Will result in { foo: 'foo', bar: 'bar'}
const item = {
foo: 'foo',
... true ? { bar: 'bar' } : {},
... false ? { falsy: 'falsy' } : {},
}
console.log(item)
In other answer I explained the same idea, but for arrays. You can check it too here.

es6: what is the value of result using spread operator?

came across this code learning redux and react.
my question is what the final result looks like when the spread oeprator is used. if I understand it correctly it basically turns iterable or array into individual arguments.
so I expect that the output is just creating another JSON object using all the fields obtained via the ... operator.
const INITIAL_STATE = { postsList: {posts: [], error:null, loading: false},
newPost:{post:null, error: null, loading: false},
activePost:{post:null, error:null, loading: false},
deletedPost: {post: null, error:null, loading: false},
};
export default function(state = INITIAL_STATE, action) {
let error;
switch(action.type) {
case FETCH_POSTS:// start fetching posts and set loading = true
return { ...state, postsList: {posts:[], error: null, loading: true} };
so is this the result of FETCH_POSTS:
{
postsList: {posts: [], error:null, loading: false},
newPost:{post:null, error: null, loading: false},
activePost:{post:null, error:null, loading: false},
deletedPost: {post: null, error:null, loading: false, },
so basically it was smart enough to know that there existed the postsList key and overwritten it?
now is it anti-pattern to rely on checking the existence of the state for the react.js app? meaning "if this key exists do this" instead of "if key value is null do this"
why don't you just change the value of the key via array key? INITIAL_STATE['postsList'] = {...postsObject}
or is using the spread operator the new pattern?
When using Redux you want to treat the state as immutable. The reducer should return a new state object, not modify the existing one. If you run INITIAL_STATE['postLists'] = somethingNew; you are modifying the object referenced by INITIAL_STATE.
Instead of using the spread operator in this case is to use Object.assign, which will create a new object based on an existing object and overload some properties.
That being said, you should not really use INITIAL_STATE at all except as the default value of state in the reducer definition.
Object.assign and ... spread operator is good for shallow copying. For deep copying, I prefer lodash/deepClone or deepCopy util like this.
export default function deepCopy(obj) {
if (typeof obj === 'object' && Object.prototype.toString.call(obj) === '[object Array]') {
let finalObj = [];
for (let i = 0, len = obj.length; i < len; ++i) {
finalObj.push(deepCopy(obj[i]));
}
return finalObj
} else if (typeof obj === 'object') {
let finalObj = {};
for (let prop in obj) {
if (obj.hasOwnProperty(prop)) {
finalObj[prop] = deepCopy(obj[prop]);
}
}
return finalObj;
} else {
return obj;
}
}
I blindly deep-copy my state to a new state. And then apply Object.assign to various objects. So, my code will be:
const newState = deepCopy(state);
Object.assign(newState.prop1, { innerProp: 'val' });

How do I determine a Keypath for a given key and iterable?

I have an Immutable.js Map that looks something like this:
item1: {
prop1: {
potato: true,
turnip: false,
ragamuffin: true
}
},
item2: {
prop1: {
petunia: true,
azalea: false,
stinkweed: true
}
}
I am writing a function that'll take a keyName and newValue. Instead of enumerating each keypath, I'd like to be able to do something like:
state = state.setIn(state.getKeyPath(keyName), newValue)
For this project, duplicate key names are not a concern (i.e., no Map I parse will have >1 instance of any keyName)
ETA: my current solution is OK for now b/c my state will never be nested >1 level deep, but would rather use a built-in method if one exists:
let keyName = 'petunia'
let keyPath
// make a List of all top-level keys, then loop through each
List(sampleMap.keys()).forEach((topLevelKey) => {
// if we find our key, assign our keyPath accordingly
if (sampleMap.hasIn([topLevelKey, keyName]) === true) {
keyPath = [topLevelKey, keyName]
})
})

Categories

Resources