Learn JavaScript in Construct, part 7: Objects

27

Index

Features on these Courses

Stats

9,112 visits, 23,102 views

Tools

Translations

This tutorial hasn't been translated.

License

This tutorial is licensed under CC BY-NC 4.0. Please refer to the license text if you wish to reuse, share or remix the content contained within this tutorial.

Published on 29 Oct, 2021. Last updated 16 Dec, 2021

Nested objects

Remember that objects can be used anywhere other values like numbers and strings can be used. This also applies to object properties, which can also have a value which is another object. Then these properties can be accessed with another dot, or with a chain of square brackets when using string syntax.

let person = {
	// The "name" property is another object
	name: {
		first: "Joe",
		last: "Bloggs"
	},
	age: 30
};

// Access the first name with dots
console.log(person.name.first);		// Joe

// Access the first name with strings
console.log(person["name"]["first"]);	// Joe

This allows us to build more complex data structures. Everything can be organised in to properties and sub-properties, instead of having to have lots of properties all on the same object.

null

We previously covered undefined, a special value JavaScript provides when something is empty or missing. Somewhat confusingly, JavaScript provides another special empty value: null.

Why are there two special empty values? This is commonly regarded as a design mistake in JavaScript. Most other programming languages only have a single special empty value, usually also named null, or something similar like nil. A good convention is to always use null whenever you as a programmer intentionally want to have an empty or unspecified value, and only use undefined where it's the JavaScript language that gives you that value. In other words you can think of undefined as something that is entirely missing, like a non-existent property, and null as something that is there, but holds an empty value, like a property that exists but is set to an empty value.

let myObject = {
	// Prefer to use null for empty properties
	myProperty: null
};

Like undefined, null is also falsy, meaning in an if statement, or when converting to a boolean, it counts as false. The JavaScript language refers to both undefined and null as nullish values, as they are both kinds of empty value.

Trying to access a property of something that is nullish will cause an error.

let obj = null;

// TypeError: Cannot read properties of null (reading 'name')
console.log(obj.name);

Note this can also happen through nested references, as shown below.

let myObject = {
	myProperty: null
};

// TypeError: Cannot read properties of null (reading 'anotherProperty')
console.log(myObject.myProperty.anotherProperty);

Since myObject.myProperty is null, the next attempt to access a property fails - in this case when trying to access anotherProperty.

Optional chaining

Instead of dots, you can use the optional chaining operator ?. - this allows you to read a property off something nullish, and it won't cause an error. It will just give you undefined at the end. Of course if a property does exist, it still returns its value. For example the following code logs undefined rather than causing an error.

let myObject = {
	myProperty: null
};

console.log(myObject?.myProperty?.anotherProperty);	// undefined

This works with entire chains of null values, hence the "chaining" in the name. The following code still logs undefined rather than causing an error, even though there isn't an object at all and none of the properties exist.

let obj = null;
console.log(obj?.firstProperty?.secondProperty?.thirdProperty);

To demonstrate how this is a useful shortcut, what if we wanted to run the above code without using optional chaining and without causing an error? We'd have to use a big 'if' statement that checks every single property exists along the way, which is a lot more code to write.

let result;

let obj = null;

// Check the object and every single expected property exists
if (obj &&
    obj.firstProperty &&
    obj.firstProperty.secondProperty &&
    obj.firstProperty.secondProperty.thirdProperty)
{
    // All properties exist: read the result
    result = obj.firstProperty.secondProperty.thirdProperty;
}

console.log(result);

The optional chaining syntax also works with string properties, although it uses a slightly odd looking ?.["property"] syntax.

let myObject = {
	myProperty: null
};

console.log(myObject?.["myProperty"]?.["anotherProperty"]);

Finally, this also extends to calling functions with func?.(). This can be used to call a function on something nullish, and instead of causing an error trying to call the function it will just return undefined.

let someFunction = null;

// Not an error even though someFunction is null,
// because optional ?.() syntax used
console.log(someFunction?.());

These are useful ways to attempt to access properties if you don't mind if any of them exist and can accept getting a nullish value instead. In general though you should know if your program expects properties to exist or not, and use the standard syntax if they should exist. So don't just go and use ?. everywhere - it won't make your code any better. Just use it in specific cases when missing properties is not a problem and the convenience is helpful.

  • 2 Comments

  • Order by
Want to leave a comment? Login or Register an account!