pwd > ninjaPixel/v2/man-drawer/javascript-objects

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])=>{ ... })