What is the enumerable argument for in Object.create? - javascript

In what usages of Object.create do you want to set enumerable to true?

A property of an object should be enumerable if you want to be able to have access to it when you iterate through all the objects properties. Example:
var obj = {prop1: 'val1', prop2:'val2'};
for (var prop in obj){
console.log(prop, obj[prop]);
}
In this type of instantiation, enumerable is always true, this will give you an output of:
prop1 val1
prop2 val2
If you would have used Object.create() like so:
obj = Object.create({}, { prop1: { value: 'val1', enumerable: true}, prop2: { value: 'val2', enumerable: false} });
your for loop would only access the prop1, not the prop2. Using Object.create() the properties are set with enumerable = false by default.

Related

How to determine if a variable is an empty object, empty array or empty string?

I have an object whose keys can have have values which can be object, string or array.
const obj = {
key1: 'asdf',
key2: {},
key3: ['a','b','c'],
key4: [],
key5: '',
}
I have to filter all the keys which are empty i.e empty objects, empty array or empty string.
Is there any other way than writing conditions based on typeof of keys and then checking null ?
Simply use Object.keys()
if (Object.keys(something).length) console.log("Not empty");
This works for all of strings, arrays, and of course, objects.
You can check the Object.keys documentation for more details, specifically, the examples section and the non-object coercion section, which state:
// simple array const arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)); // console: ['0', '1', '2']
// array-like object const obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']
and
In ES5, if the argument to this method is not an object (a
primitive), then it will cause a TypeError.
From ES2015 onwards, a non-object argument will be coerced to an
object.
// In ES5 Object.keys('foo'); // TypeError: "foo" is not an object
// In ES2015+ Object.keys('foo'); // ["0", "1", "2"]
For empty strings simple compare the property to "".
comparing for empty objects and empty arrays can be done using comparing the Object.keys of the property length compared to 0.
const obj = {
key1: 'asdf',
key2: {},
key3: ['a','b','c'],
key4: [],
key5: '',
}
function filterObject(obj) {
for (var propName in obj) {
if (obj[propName] === "" || Object.keys(obj[propName]).length === 0) {
delete obj[propName];
}
}
return obj;
}
console.log(filterObject(obj));
You can easily achieve the result using Object.keys and reduce
Loop over the keys of object obj using Object.keys and set the properties of new object only if it is not empty
You can check for the type of value using typeof operator
1) string: typeof will return string and check for the length property
2) array: You can use Array.isArray and it will return boolean value whether the object is an array or not
3) object: You can first get all keys using Object.keys and then check if there is any property exist on that object or not using length property
Since, arrays are object in JS, so you can directly use
(typeof value === "object" && Object.keys(value).length)
to check where it is an object and if it contain any property or not
You can create a filter as
if (value !== "" && Object.keys(value).length) acc[key] = value;
const obj = {
key1: "asdf",
key2: {},
key3: ["a", "b", "c"],
key4: [],
key5: "",
};
const result = Object.keys(obj).reduce((acc, key) => {
const value = obj[key];
if (value !== "" && Object.keys(value).length) acc[key] = value;
return acc;
}, {});
console.log(result);
const obj = {
key1: 'asdf',
key2: {},
key3: ['a','b','c'],
key4: [],
key5: '',
}
Object.keys(obj).map(dt => {if (!obj[dt].length) delete obj[dt]});
console.log(obj)

Why {...window}.Math is undefined?

const a = {x:1};
console.log({...a}.x);
// 1
console.log(window.Math);
// Math {abs: ƒ, acos: ƒ, acosh: ƒ, asin: ƒ, asinh: ƒ, …}
console.log({...window}.Math);
// undefined
I don't understand why {...a}.x evaluates 1, but {...window}.Math evaluates undefined.
That's because Math is not enumerable.
The ECMA-2018 (ES9) specs is a little hard to read. MDN and a proposal page stated: {...obj} creates a new object for all of obj's (1) own and (2) enumerable properties. Math is window's own property, but not enumerable:
console.log(window.hasOwnProperty("Math"));
console.log(Object.getOwnPropertyDescriptor(window, "Math"));
You can reproduce the situation with an object:
obj = {};
Object.defineProperty(obj, 'x', {
enumerable: false,
value: 123
});
console.log(obj); // Google Chrome prints { x: 123 } but StackOverflow prints {} and Node.js prints {} too
console.log(obj.x);
console.log(({...obj}).x);
{...Window} is using the spread operator to make a copy into a new object. Math is not enumerable so it won't be copied to the new object.
You can test it by your self:
const a = {}
Object.defineProperty(a, "prop1", { value: "enumerable", enumerable: true })
Object.defineProperty(a, "prop2", { value: "not enumerable", enumerable: false })
Then copy your object:
{...a}.prop1 //"enumerable"
{...a}.prop2 // undefined

Given an object how can I get the name used to describe it?

I have a JSON object(mainObj) which in turn has objects (say obj1, obj2, obj3). What I am trying to achieve is when I check for a condition iterating through every obj in the mainObj and if it holds true, I want to add only the name of that obj in an array of String. Something like,
for(obj in mainObj){
if(obj holds condition){
add the descriptor of the obj (in string format) to an array (not the entire obj)
}
You can use Object.keys() to iterate over your object keys, then use Array.filter() to filter the keys, here I am checking if the inner objects have a property show and if this property is truthy:
const mainObj = {
obj1: { show: true, a: 1 },
obj2: { show: false, a: 2 },
obj3: { a: 3 },
obj4: { show: true, b: 1 }
};
const result = Object.keys(mainObj).filter(key => mainObj[key].show);
console.log(result);
If you want to use a for-in loop, you have to make sure the property is part of the object and is not inherited from its protype chain using Object.hasOwnProperty():
const mainObj = {
obj1: { show: true, a: 1 },
obj2: { show: false, a: 2 },
obj3: { a: 3 },
obj4: { show: true, b: 1 }
};
const result = [];
for (const prop in mainObj) {
if (mainObj.hasOwnProperty(prop) && mainObj[prop].show) {
result.push(prop);
}
}
console.log(result);

Why does JSON.stringify returns "{}"? [duplicate]

This question already has answers here:
Why is JSON.stringify not serializing prototype values?
(3 answers)
Closed 8 years ago.
I have one prototype of this structure:
function MyObj() { }
MyObj.prototype = {
prop1: {
prop11: null,
prop12: null,
prop13: null,
},
prop2: {
prop21: null,
prop22: null,
prop23: null,
},
prop3: {
prop31: [],
prop32: '',
prop34: [],
},
prop4: {
prop41: null,
},
}
When I call JSON.stringify(myObjInstance), I get {}, why?
This happens because prop1 through prop4 are properties of the prototype and not of the instantiated object.
You can compare it to something like:
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
// add property to bag
}
}
Only the properties of the object itself are used.
Because JSON.stringify only includes an object's own properties (specifically, own enumerable properties), not properties an object inherits from its prototypes or any of its own properties that are non-enumerable.
So for example: Live Copy | Live Source
function Foo() { }
Foo.prototype.inherited = true;
var f = new Foo();
Object.defineProperty(f, "ownNonEnumerable", {
value: true
});
f.ownEnumerable = true;
console.log(f.inherited); // true
console.log(f.ownNonEnumerable); // true
console.log(f.ownEnumerable); // true
console.log(JSON.stringify(f)); // {"ownEnumerable": true}
JSON.stringify will only include a property if it can be found with hasOwnProperty, and
new MyObj().hasOwnProperty("prop1") === false;
Try this instead:
JSON.stringify(MyObj.prototype);
JSON.stringify(obj) will output the properties that the object itself has, not its prototype. Its prototype is a different object.
What you're looking for would be something like this
JSON.stringify(MyObj.prototype)
I had the same issue, when I start working with JSON.stringify. In order to create a string the object you create needs to of typeof == object. Otherwise it won't work.
Try to alert the typeOf of MyObj. If it not Object that won't work.
MyObj = {
prop1: {
prop11: null,
prop12: null,
prop13: null,
},
prop2: {
prop21: null,
prop22: null,
prop23: null,
},
prop3: {
prop31: [],
prop32: '',
prop34: [],
},
prop4: {
prop41: null,
},
}

Why does JSON.stringify not serialize non-enumerable properties?

I'm serializing objects to JSON strings with JavaScript,
I noticed only enumerable object properties get serialized:
var a = Object.create(null,{
x: { writable:true, configurable:true, value: "hello",enumerable:false },
y: { writable:true, configurable:true, value: "hello",enumerable:true }
});
document.write(JSON.stringify(a)); //result is {"y":"hello"}
[pen]
I'm wondering why that is? I've searched through the MDN page, the json2 parser documentation.
I could not find this behavior documented any-where.
I suspect this is the result of using for... in loops that only go through [[enumerable]] properties (at least in the case of json2). This can probably be done with something like Object.getOwnPropertyNames that returns both enumerable, and non-enumerable properties.
That might be problematic to serialize though (due to deserialization).
tl;dr
Why does JSON.stringify only serialize enumerable properties?
Is this behavior documented anywhere?
How can I implement serializing non-enumerable properties myself?
It's specified in the ES5 spec.
If Type(value) is Object, and IsCallable(value) is false
If the [[Class]] internal property of value is "Array" then
Return the result of calling the abstract operation JA with argument value.
Else, return the result of calling the abstract operation JO with argument value.
So, let's look at JO. Here's the relevant section:
Let K be an internal List of Strings consisting of the names of all the own properties of value whose [[Enumerable]] attribute is true. The ordering of the Strings should be the same as that used by the Object.keys standard built-in function.
As #ThiefMaster answered above, it's specified in the spec
however, if you know the names of the non-enumerable properties you like to be serialized ahead of time, you can achieve it by passing a replacer function as the second param to JSON.stringify() (documentation on MDN), like so
var o = {
prop: 'propval',
}
Object.defineProperty(o, 'propHidden', {
value: 'propHiddenVal',
enumerable: false,
writable: true,
configurable: true
});
var s = JSON.stringify(o, (key, val) => {
if (!key) {
// Initially, the replacer function is called with an empty string as key representing the object being stringified. It is then called for each property on the object or array being stringified.
if (typeof val === 'object' && val.hasOwnProperty('propHidden')) {
Object.defineProperty(val, 'propHidden', {
value: val.propHidden,
enumerable: true,
writable: true,
configurable: true
});
}
}
return val;
});
console.log(s);
You can achieve a generic JSON.stringify() that includes non-enumerables with code like below. But first, some notes:
This code has not been thoroughly tested for possible bugs or unexpected behavior; also, it turns functions into basic objects. Treat accordingly.
copyEnumerable() is the function to pay attention to. It does not itself internally call JSON.stringify() (you can do that yourself, with the output object) mainly because recursive calls to JSON.stringify() (due to nested objects) will yield uglier, unwanted results.
I'm only checking for object and function types to be treated specially; I believe that these are the only types which need to be handled specially... Note that JS Arrays ([]) fall into this category (objects), as well as classes (functions), and null does not. Since null is of type object, I included the !! check.
The stopRecursiveCopy Set is just to make sure it's not doing infinite recursion.
I'm using a trick with the 2 extra parameters to JSON.stringify() calls to make it output something formatted prettier, for easy reading.
The code is in an easy format to try out and tweak in the console:
{
o = {};
d = {};
Object.defineProperties(o, {
a: {
value: 5,
enumerable: false
},
b: {
value: "test",
enumerable: false
},
c: {
value: {},
enumerable: false
}
});
Object.defineProperty(o.c, "d", {
value: 7,
enumerable: false
});
//The code to use (after careful consideration!).
function isObject(testMe) {
return ((typeof(testMe) === "object" && !!testMe) ||
typeof(testMe) === "function");
}
let stopRecursiveCopy = new Set();
function copyEnumerable(obj) {
if (!isObject(obj)) {
return obj;
}
let enumerableCopy = {};
for (let key of Object.getOwnPropertyNames(obj)) {
if (isObject(obj[key])) {
if (!stopRecursiveCopy.has(obj[key])) {
stopRecursiveCopy.add(obj[key]);
enumerableCopy[key] = copyEnumerable(obj[key]);
stopRecursiveCopy.delete(obj[key]);
}
} else {
enumerableCopy[key] = obj[key];
}
}
return enumerableCopy;
}
console.log(JSON.stringify(copyEnumerable(o), null, " "));
Object.defineProperty(copyEnumerable, "test", {
value: 10,
enumerable: false
});
console.log(JSON.stringify(copyEnumerable(copyEnumerable), null, " "));
Object.defineProperty(o, "f", {
value: copyEnumerable,
enumerable: false
});
console.log(JSON.stringify(copyEnumerable(o), null, " "));
}
which outputs:
//o
{
"a": 5,
"b": "test",
"c": {
"d": 7
}
}
//copyEnumerable itself
{
"test": 10,
"prototype": {
"constructor": {
"test": 10,
"length": 1,
"name": "copyEnumerable"
}
},
"length": 1,
"name": "copyEnumerable"
}
//o again, but with `f: copyEnumerable` added
{
"a": 5,
"b": "test",
"c": {
"d": 7
},
"f": {
"test": 10,
"prototype": {},
"length": 1,
"name": "copyEnumerable"
}
}
Pertinent links:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof
https://stackoverflow.com/a/14706877/1599699
https://stackoverflow.com/a/18538851/1599699

Categories

Resources