In JavaScript, the object
constructor lets us create object wrapper with the given values. It will create an empty object if null
or undefined
is passed into the object
constructor. If the value passed into the constructor is an object already, then it will return the object.
The object
constructor has two properties. It has a length
property that is always 1, and like all other objects, the object
constructor has a prototype to get all the property additions to the type object
.
Continuing from Part 1, the object
constructor has many useful methods that can be used without constructing a new object.
Object.fromEntries()
The Object.fromEntries()
method accepts an array with arrays of key-value pairs with the key as the first element and the value of the corresponding key as the second element. We can also pass in other iterables with the same kind of arrays into the method.
For example, we can write the following code to pass in an array of key-value pairs to create an object:
const entries = [
[
['a', 1],
['b', 2]
]
];
const obj = Object.fromEntries(entries);
console.log(obj);
The resulting object obj
should be {a: 1, b: 2}
as we can see from the console.log
output when the code above is run. We can pass in other iterables, such as maps:
const entries = new Map([
['a', 1],
['b', 2]
]);
const obj = Object.fromEntries(entries);
console.log(obj);
We should see the same thing logged. Also, we can convert arrays to objects with the following code:
const arr = [1,2,3];
const entries = arr.map((value, index) => [index, value]);
const obj = Object.fromEntries(entries);
console.log(obj);
When the code above is run, we get {0: 1, 1: 2, 2: 3}
since we mapped the index of each array entry to the key and the value of each array entry to the value.
Object.getOwnPropertyDescriptor()
The Object.getOwnPropertyDescriptor()
method gets the property descriptor of a property in the object and returns it. As the name suggests, it only gets the property descriptor of the object that’s in the object itself and not up the prototype chain.
A property descriptor is an object with the property names as keys and the properties writable
, configurable
, enumerable
, and value
as properties of the property name keys.
writable
means that the property’s value can be changed. configurable
means the property descriptor may be changed and if the property can be deleted from the object.
The enumerable
property means that the property shows up during enumeration of the properties with the for...in
loop and value
is the value of the property.
For example, if we log a property descriptor of an object with:
const obj = {
a: 1
}const descriptor = Object.getOwnPropertyDescriptor(obj, 'a');
console.log(descriptor);
We get {value: 1, writable: true, enumerable: true, configurable: true}
. The value
is the value of the property and the rest of the properties are the property descriptor’s properties. If we have property getters and setters, they are also returned with the method call:
let obj = {};
let value;
Object.defineProperty(obj, 'a', {
get() {
return value;
},
set(a) {
value = a;
}
});
const descriptor = Object.getOwnPropertyDescriptor(obj, 'a');
console.log(descriptor);
When the code above is run, we get the get
and set
functions back in the descriptor object.
Object.getOwnPropertyDescriptors()
While the Object.getOwnPropertyDescriptor()
gets the property descriptor for a single object, the Object.getOwnPropertyDescriptors()
gets all the property descriptors of an object in one object—once again without the properties that are inherited by this object up the prototype chain, with the property names as the keys and the property descriptor of the corresponding property name key as the value.
For example, if we have
const obj = {
a: 1,
b: 2
}
const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors);
then we get
{
"a": {
"value": 1,
"writable": true,
"enumerable": true,
"configurable": true
},
"b": {
"value": 2,
"writable": true,
"enumerable": true,
"configurable": true
}
}
We get all the values and the property descriptor attributes of each property. Like with Object.getOwnPropertyDescriptor()
, this method has the same definitions for the property descriptors.
A property descriptor is an object with the property names as keys and the properties writable
, configurable
, enumerable
, and value
as properties of the property name keys.
The writable
means that the property’s value can be changed. configurable
means the property descriptor may be changed and if the property can be deleted from the object.
The enumerable
property means that the property shows up during enumeration of the properties with the for...in
loop, and value
is the value of the property.
Object.getOwnPropertyNames()
The Object.getOwnPropertyNames()
method returns an array of property names that are defined in the object itself and not in any object up the prototype chain. Non-enumerable properties are also returned except for those that are symbols. For example, if we have:
let obj = {
a: 1,
b: 2
}
Object.defineProperty(obj, 'c', {
"value": 2,
"writable": true,
"enumerable": false,
"configurable": true
})
const names = Object.getOwnPropertyNames(obj);
console.log(names);
If we run the code above, we get [“a”, “b”, “c”]
returned since all properties defined except ones identified with Symbols are returned. If we have Symbols in our object, we won’t see it in the returned array. For example, if we have
let obj = {
a: 1,
b: 2,
[Symbol('foo')]: 3
}
Object.defineProperty(obj, 'c', {
"value": 4,
"writable": true,
"enumerable": false,
"configurable": true
})
const names = Object.getOwnPropertyNames(obj);
console.log(names);
we still see [“a”, “b”, “c”]
if we run the code above since symbols aren’t included in the array. We have the Object.getOwnPropertySymbols()
to get properties that are identified with symbols.
Object.getOwnPropertySymbols()
The Object.getOwnPropertySymbols()
returns an array of symbols that are used as identifiers in the properties of an object.
It only gets the property identifiers that are named with symbols and nothing else.
Also, it doesn’t traverse up to the prototype chain to get properties of objects up the prototype chain. For example, if we have the following code:
let obj = {
a: 1,
b: 2,
[Symbol('foo')]: 3
}
Object.defineProperty(obj, 'c', {
"value": 4,
"writable": true,
"enumerable": false,
"configurable": true
})
const symbols = Object.getOwnPropertySymbols(obj);
console.log(symbols);
we get [Symbol(foo)]
logged as we expected.
Object.getPrototypeOf()
The Object.getPrototypeOf()
method gets the prototype of an object, which is the same as the [[Prototype]]
property of the specified object. For example, if we have
const prototype1 = {};
const obj = Object.create(prototype1);
console.log(Object.getPrototypeOf(obj) === prototype1);
the console.log
will output true
since the first argument of Object.create
is the prototype object for the object that it returns; likewise, if we use Object.setPrototypeOf()
method to set the prototype of an existing object.
const prototype2 = {};
let obj2 = {};
Object.setPrototypeOf(obj2, prototype2);
console.log(Object.getPrototypeOf(obj2) === prototype2);
The console.log
will also output true
since set the prototype of obj2
explicitly.
Object.is()
The Object.is()
method compares whether two objects passed in its argument are the same value. Two values are the same if they’re:
- both
undefined
- both
null
- both
true
or bothfalse
- both strings with the same length and same characters in the same order
- both objects having the same references
- both numbers and both
+0
, or both-0
or bothNaN
or both non-zero and both notNaN
and they both have the same value
It doesn’t convert types like the ==
operator to compare objects and don’t convert truthy or falsy values to booleans. Also, it’s not the same as comparing objects with the ===
operator because with the ===
operator, -0
and +0
are equal and NaN
is not the same as itself.
For example, we can make the following comparisons:
Object.is('a', 'a'); // true
Object.is(document, document); // true
Object.is('a', 'b'); // false
Object.is([], []); // false
const obj = { a: 1 };
const obj2 = { a: 1 };
Object.is(obj, obj); // true
Object.is(obj, obj2); // false
Object.is(null, null); // true
Object.is(0, -0); // false
Object.is(-0, -0); // true
Object.is(NaN, 0/0); // true
As we can see, only object references and values are compared. The object’s content isn’t compared, so even if two objects have the same content, they’re still considered different since their references in memory are different, as they’re defined by two different variables.
Object.isExtensible()
The Object.isExtensible()
method checks whether an object’s property is extensible. That is if an object can have new properties added to it. For example, if we have:
const obj = {};
const obj2 = {};console.log(Object.isExtensible(obj));Object.preventExtensions(obj2);console.log(Object.isExtensible(obj2));
Then console.log(Object.isExtensible(obj));
will log true
since we didn’t explicitly prevent adding new properties to obj
. However, console.log(Object.isExtensible(obj2));
will log false
since we called Object.preventExtensions(obj2);
to prevent new properties from being added to obj2
.
If we run the following code, we also get false
logged in both console.log
statements because we explicitly prevented the objects obj
and obj2
from having new properties added with the Object.freeze()
and Object.seal()
methods respectively:
const obj = {};
const obj2 = {};
Object.freeze(obj);
console.log(Object.isExtensible(obj));Object.seal(obj2);
console.log(Object.isExtensible(obj2));
Object.isFrozen()
The Object.isFrozen()
method determines if an object is frozen. Frozen means that all properties’ values in an object can’t be changed. Also, new properties can’t be added to it, and existing property descriptors for the frozen object can’t be changed. The object is frozen in place. This method doesn’t return a new frozen object. Instead, it returns the original object before it’s frozen. The frozen object’s prototype also can’t be changed. For example, if we have:
const obj = {
a: 1
};console.log(Object.isFrozen(obj));
We get false
from the console.log
statement because we didn’t explicitly freeze the object by calling Object.freeze(obj)
. On the other hand, we have frozen the object, then Object.isFrozen()
will return true
. For example:
const obj = {
a: 1
};Object.freeze(obj);
console.log(Object.isFrozen(obj));
Then we get true
from the console.log
statement because we froze the object by calling Object.freeze(obj)
. If primitive values are passed in, Object.isFrozen()
will return true
since they’re immutable. For example, if we have:
console.log(Object.isFrozen(1));
console.log(Object.isFrozen('string'));
console.log(Object.isFrozen(null));
console.log(Object.isFrozen(undefined));
console.log(Object.isFrozen(NaN));
console.log(Object.isFrozen(true));
console.log(Object.isFrozen(Symbol('a')));
Then all the console.log
statement will be true
since they’re all immutable.
The Object
constructor has many more methods for constructing objects from an array of an array with key and value of properties and also methods to get property descriptors from objects, property names, property symbols, gets the keys of an object and prevent properties from being added or deleted or modify their property descriptors.