JavaScript objects
Published
Create
object.create(null)
will make an object without a prototype. After you've created it, you can add props to it:
const obj1 = Object.create(null)
obj1.make = "Ford"
obj1.model = "Ka"
obj1.toString() // obj1.toString is not a function
Conversely, if you want an object to inherit its prototype from somewhere else, then you can do this:
const obj2 = Object.create(obj1)
obj2.make // "Ford"
Freeze
object.freeze
will stop an object from being modified
const obj3 = {make: "VW", model: "Golf R"};
Object.freeze(obj3);
obj3.year = "1999"; // Cannot add property year, object is not extensible
obj3.make = "Ford"; // Cannot assign to read only property 'make' of object
Clone
structuredClone
creates a deep clone of a given value. This is a global available on the window
and in NodeJS. The
introduction of this API means that you don’t have to use lodash’s cloneDeep
method, but note
that some things don’t work with structured clone
, of particular note is:
- Function objects
- DOM nodes
- The prototype chain is not walked or duplicated
const obj1 = {make: "VW", model: "Golf R", engine: {size: 2}};
const obj2 = {...obj1};
obj2.engine.size = 3; // 3. We've edited the OG object, which we didn't want to do.
Using structuredClone
instead:
const obj1 = {make: "VW", model: "Golf R", engine: {size: 2}};
const obj2 = structuredClone(obj1);
obj2.engine.size = 3;
console.log(obj1.engine.size); // 2 ✌️
Accessing keys
If you're using TypeScript, then you'll use in
every day:
if (myKey in myObj) {
// do stuff
}
Optional chaining
This is a nice quality-of-life feature that lets you do away with long, speculative checks of property existence on objects.
const baz = foo?.bar?.baz
// if you can't use dot notation to access the prop, you can do this instead
const baz2 = foo?.bar?.["baz 2"]
You can also use optional chaining to execute a function if it exists on the object. If the function doesn’t exist, it
will resolve to undefined
(like it would for a regular property).
const width = house?.bedroom?.getWidth?.()
TypeScript
Sometimes I will declare the type of an object, using the Record
type, and I need to initialise the object as an empty
object (which, in my mind at least, seems valid). TypeScript doesn’t allow this, so in this instance use Partial
too.
e.g.
export type ColumnSearchValue = string | undefined;
export type ColumnSearches<RowRec = RowRecord> = Record<DataTableColumn<RowRec>['id'],
ColumnSearchValue>;
const relevantColumnSearches: Partial<ColumnSearches<T>> = {};
lodash iterator alternatives
rather than pulling in lodash’s _.forEach
, _.map
, _.filter
etc. methods, use Object.entries()
instead. i.e.:
Object.entries(myObj).forEach(([key, value])=>{ ... })
Object.entries(myObj).map(([key, value])=>{ ... })
Object.entries(myObj).filter(([key, value])=>{ ... })