Home Chapter 6: Objects
Post
Cancel

Chapter 6: Objects

Introduction

Objects are the most important part of JavaScript. They are the core of the language. Most of the time, you will be working with objects. Objects are used to represent real-world things, like a user, a photo, a car, etc. They are used to store data, and they are used to perform actions. Objects are used to group related variables and functions together into a single entity. In this chapter, you will learn how to create objects and how to access their properties and methods.

What is an Object?

An object is a collection of related data and/or functionality (which usually consists of several variables and functions — which are called properties and methods when they are inside objects.)

Creating an Object

There are two ways to create an object: using an object literal, and using the Object() constructor.

Using an Object Literal

This is the easiest way to create an object. An object literal is a list of name:value pairs (like age:50) inside curly braces {}.

The following example creates a new JavaScript object with four properties:

1
2
3
4
5
6
var person = {
  firstName: "John",
  lastName: "Doe",
  age: 50,
  eyeColor: "blue"
};

Using the JavaScript Keyword new

The following example also creates a new JavaScript object with four properties:

1
2
3
4
5
var person = new Object();
person.firstName = "John";
person.lastName = "Doe";
person.age = 50;
person.eyeColor = "blue";

Object.create()

The Object.create() method creates a new object, using an existing object as the prototype of the newly created object.

1
2
3
4
5
6
7
8
9
10
var person = {
  firstName: "John",
  lastName: "Doe",
  age: 50,
  eyeColor: "blue"
};

var x = Object.create(person);
x.firstName = "John";
x.age = 10;

To create an object with a single, specified property, and an empty prototype (proto):

1
var person = Object.create({ firstName: "John" });

Pass in null to create a new object with no prototype (proto):

1
var person = Object.create(null);

To create an ordinary empty object, pass Object.prototype:

1
var person = Object.create(Object.prototype);

One use for Object.create() is when you want to guard against unintended (but nonmalicious) modification of an object by a library function that you don’t have control over.

1
2
3
4
5
let x = { a: 1 }; // x has an own property a.
let y = Object.create(x); // y inherits property a.
let z = Object.create(y); // z inherits property a.
y.a++; // Increments y's inherited property; x is unaffected.
z.a++; // Increments z's inherited property; x and y are unaffected.

Querying and Setting Properties

You can query and set properties in two ways: dot notation and bracket notation.

Dot Notation

You can access object properties in two ways:

1
objectName.propertyName

or

1
objectName["propertyName"]

The following example uses dot notation to access the firstName property of the person object:

1
2
3
4
5
6
7
8
var person = {
  firstName: "John",
  lastName: "Doe",
  age: 50,
  eyeColor: "blue"
};

var x = person.firstName;

Bracket Notation

The following example uses bracket notation to access the firstName property of the person object:

1
2
3
4
5
6
7
8
var person = {
  firstName: "John",
  lastName: "Doe",
  age: 50,
  eyeColor: "blue"
};

var x = person["firstName"];

Inheritance

Inheritance is a powerful feature of object-oriented programming. It refers to the ability of an object to access methods and other properties from another object.

Inheritance in JavaScript works through something called prototypes and prototype chains. Every object in JavaScript has a prototype (which could be another object or null). When trying to access a property or method on an object, the property will not only be sought on the object but on the prototype of the object, the prototype of the prototype, and so on until either a property with a matching name is found or the end of the prototype chain is reached.

1
2
3
4
5
6
7
8
9
var person = {
  firstName: "John",
  lastName: "Doe",
  age: 50,
  eyeColor: "blue"
};

var m = Object.create(person);
m.firstName = "Luke"; // m inherits the property firstName from person. Value is "Luke". person.firstName is still "John"

Property access errors

If you try to access an undefined property, you will get a undefined value.

1
2
3
4
5
6
7
8
9
10
11
var person = {
  firstName: "John",
  lastName: "Doe",
  age: 50,
  eyeColor: "blue"
};

var x = person.firstName; // person.firstName is the property. Value is "John"
var y = person.lastName; // person.lastName is the property. Value is "Doe"

var q = person.middleName; // undefined

However, if you try to access a property that doesn’t exist at all, you will get a TypeError stating that the property doesn’t exist.

1
2
3
4
5
6
7
8
9
10
11
12
var person = {
  firstName: "John",
  lastName: "Doe",
  age: 50,
  eyeColor: "blue"
};

var x = person.firstName; // person.firstName is the property. Value is "John"
var y = person.lastName; // person.lastName is the property. Value is "Doe"

var q = person.middleName; // undefined
var length = person.middleName.length; // Uncaught TypeError: Cannot read property 'length' of undefined

To cater for null and undefined values, you can use the && operator.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var person = {
  firstName: "John",
  lastName: "Doe",
  age: 50,
  eyeColor: "blue"
};

var x = person.firstName; // person.firstName is the property. Value is "John"
var y = person.lastName; // person.lastName is the property. Value is "Doe"

var q = person.middleName; // undefined
var length = person.middleName && person.middleName.length; // undefined

// or use the conditional property access operator
var length = person.middleName?.length; // undefined

The rules that specify when a property assignment succeeds and when it fails are intuitive but difficult to express concisely. An attempt to set a property p of an object o fails in these circumstances:

  • o has an own property p that is read-only: such as the global object’s built-in properties, such as Infinity, undefined, and NaN.
  • o has an inherited property p that is read-only: such as Object.prototype.
  • o does not have an own property p; o does not inherit a property p with a setter method, and o’s extensible attribute is false. (It is therefore impossible to add properties to the object.)

All other attempts to set a property succeed. When a property assignment fails, either in strict mode or in nonstrict mode, a TypeError is thrown.

Deleting Properties

The JavaScript operator delete removes a property from an object. It does not touch any of the objects in the prototype linkage. It does not remove a property from an object’s prototype. It just removes the property from the object itself. If the object inherits a property with the same name from its prototype, that inherited property will now be visible again, instead of the deleted property.

1
2
3
4
5
var o = { x: 1 }; // o has own property x and inherits property toString
delete o.x; // Delete x, and return true.
delete o.x; // Do nothing (x doesn't exist), and return true.
delete o.toString; // Do nothing (toString isn't an own property), and return true.
delete 1; // Nonsense, but evaluates to true

Testing Properties

The in operator expects a property name (as a string) on its left side and an object on its right. It returns true if the object has an own property or an inherited property by that name:

1
2
3
4
var o = { x: 1 };
"x" in o; // true: o has an own property "x"
"y" in o; // false: o doesn't have a property "y"
"toString" in o; // true: o inherits a toString property

The hasOwnProperty() method of an object tests whether that object has an own property with the given name.

1
2
3
4
var o = { x: 1 };
o.hasOwnProperty("x"); // true: o has an own property x
o.hasOwnProperty("y"); // false: o doesn't have a property y
o.hasOwnProperty("toString"); // false: toString is an inherited property

The propertyIsEnumerable() method of an object tests whether the given property name exists in the object and whether it is enumerable. Enumerable properties are those that show up in for/in loops.

1
2
3
4
5
var o = Object.create({ y: 1 }); // o inherits the property y
o.x = 1;
o.propertyIsEnumerable("x"); // true: o has an own enumerable property x
o.propertyIsEnumerable("y"); // false: y is inherited
Object.prototype.propertyIsEnumerable("toString"); // false: not enumerable

The propertyIsEnumerable() method is like the in operator, except that it does not check for properties in the prototype chain.

Enumerating Properties

The for/in loop is the only cross-platform way to enumerate the names of the properties of an object.

1
2
3
4
var o = { x: 1, y: 2, z: 3 }; // Three enumerable own properties
o.propertyIsEnumerable("toString"); // => false: not enumerable
for (p in o) // Loop through the properties
  console.log(p); // Prints x, y, and z, but not toString

The for/in loop enumerates properties in an apparently random order, and it enumerates properties that are inherited from the prototype chain along with those that are defined directly on the object.

1
2
3
4
5
var o = { x: 1, y: 2, z: 3 }; // Three enumerable own properties
o.propertyIsEnumerable("toString"); // => false: not enumerable
var properties = [];
for (p in o) properties.push(p);
properties; // => ["x", "y", "z"]; the order is not guaranteed

If you want to enumerate only the properties defined in an object, without enumerating its inherited properties, you can do so by using the Object.keys() function defined in ECMAScript 5.

1
2
var o = { x: 1, y: 2, z: 3 }; // Three enumerable own properties
Object.keys(o); // => ["x", "y", "z"]: array of own enumerable properties

If you want to enumerate all properties, including those that are not enumerable and those that are inherited, you can use the Object.getOwnPropertyNames() function defined in ECMAScript 5.

1
2
var o = { x: 1, y: 2, z: 3 }; // Three enumerable own properties
Object.getOwnPropertyNames(o); // => ["x", "y", "z"]: array of own properties

Property enumeration order

ES5 defines the order in which the names of object properties are returned when you iterate with a for/in loop or call Object.keys() or Object.getOwnPropertyNames().

  • First, the properties whose names are nonnegative integers are returned in sorted, ascending order.
  • Next, the remaining string property names are returned in the order in which they were added to the object.
  • Finally, the properties whose names look like Symbols are returned in the order in which they were added to the object.

Extending Objects

The Object.assign() function, defined in ECMAScript 2015, is used to copy the values of all enumerable own properties from one or more source objects to a target object. It returns the target object.

1
2
3
4
var target = { x: 1 }, source = { y: 2, z: 3 };
Object.assign(target, source); // => target object has properties x, y, and z
target; // => {x: 1, y: 2, z: 3}
source; // => {y: 2, z: 3}

If the target object already has a property of the same name, then the source object property overwrites it.

1
2
var o = {x: 1};
Object.assign(o, {x: 2, y: 3}, {y: 4, z: 5}); // => {x: 2, y: 4, z: 5}

You could also use the spread operator (…) to achieve the same result.

1
2
3
4
var o = {x: 1};
var p = {x: 2, y: 3};
var q = {y: 4, z: 5};
var obj = {...o, ...p, ...q}; // => {x: 2, y: 4, z: 5}

Serializing Objects

The JSON.stringify() function converts an object or value to a JSON string representation:

1
2
3
var o = {x: 1, y: {z: [false, null, ""]}}; // Define a test object
var s = JSON.stringify(o); // s == '{"x":1,"y":{"z":[false,null,""]}}'
var p = JSON.parse(s); // p == {x: 1, y: {z: [false, null, ""]}}

The JSON.stringify() function takes two additional arguments that can be used to customize the serialization. The first argument is an array of strings or numbers that specifies the set of properties that should be serialized. The second argument is a replacer function that can be used to further customize the serialization.

Object Methods

The toString() method defined by Object.prototype returns a string representation of the receiver.

1
2
var o = {x: 1, y: 2};
o.toString(); // => "[object Object]"

The toLocaleString() method defined by Object.prototype returns a localized string representation of the receiver. The toLocaleString() method is not implemented by most objects, but the top-level Array and Date objects provide their own versions of this method.

1
2
3
var date = new Date();
date.toString(); // => "Fri Apr 12 2019 11:30:00 GMT-0700 (Pacific Daylight Time)"
date.toLocaleString(); // => "4/12/2019, 11:30:00 AM" (en-US)

The valueOf() method defined by Object.prototype returns the value of the object as a JavaScript value.

1
2
var o = {x: 1};
o.valueOf(); // => {x: 1}

The toJSON() method defined by Object.prototype is a to-JSON conversion function that is used internally by JSON.stringify().

1
2
3
var o = {x: 1, y: {z: [false, null, ""]}}; // Define a test object
o.toJSON(); // => '{"x":1,"y":{"z":[false,null,""]}}'
JSON.stringify(o); // => '{"x":1,"y":{"z":[false,null,""]}}'

Extended Object Literal Syntax

The object literal syntax described in the previous section is sufficient for creating simple objects, but it does not allow you to specify the prototype or initial properties of the object.

1
2
3
var o1 = Object.create({x: 1, y: 2}); // o1 inherits properties x and y.
var o2 = Object.create(null); // o2 inherits no props or methods.
var o3 = Object.create(Object.prototype); // o3 is like {} or new Object().

Property Value Shorthand

If you are defining an object literal whose property values come from local variables, you can use a shorthand syntax that omits the colon and the value for properties whose names match local variables.

1
2
3
let x = 1, y = 2;
let o = {x, y}; // Shorthand for {x: x, y: y}
o.x + o.y // => 3

Computed Property Names

If you want to use an expression for a property name, you must use the traditional property definition syntax with a colon.

1
2
3
4
5
6
let frequency = 3;
let o = {
    ["fizz" + frequency]: "Fizz",
    ["buzz" + frequency]: "Buzz"
};
o["fizz3"] + o["buzz3"] // => "FizzBuzz"

Symbols as Property Names

If you want to use a Symbol value as a property name, you must use the traditional property definition syntax with a colon.

1
2
3
4
5
const extension = Symbol("my extension symbol");
let o = {
    [extension]: { /* extension data stored in this object */ }
};
o[extension]

In JavaScript, symbols are a unique data type introduced in ECMAScript 2015 (ES6). They are often used as property names for creating non-enumerable properties and avoiding naming conflicts. Here’s a quick overview of using symbols as property names:

  1. Creating Symbols:
    1
    
    const mySymbol = Symbol();
    
  2. Using Symbols as Property Names:
    1
    2
    3
    4
    5
    
    const obj = {};
    const mySymbol = Symbol('mySymbol');
    
    obj[mySymbol] = 'Value assigned to symbol property';
    console.log(obj[mySymbol]); // Output: Value assigned to symbol property
    
  3. Symbol Description: Symbols can have an optional description that provides a human-readable string, but it doesn’t affect the symbol’s uniqueness.
    1
    2
    
    const mySymbol = Symbol('mySymbol');
    console.log(mySymbol.description); // Output: mySymbol
    
  4. Symbol Uniqueness: Each symbol created using Symbol() or Symbol(description) is unique. Even if two symbols have the same description, they are different symbols.
    1
    2
    3
    4
    
    const symbol1 = Symbol('mySymbol');
    const symbol2 = Symbol('mySymbol');
    
    console.log(symbol1 === symbol2); // Output: false
    
  5. Symbol as Object Property: Symbols are non-enumerable by default, meaning they won’t appear in for...in loops or Object.keys().
    1
    2
    3
    4
    5
    6
    7
    
    const obj = {};
    const mySymbol = Symbol('mySymbol');
    
    obj[mySymbol] = 'Value assigned to symbol property';
    
    console.log(Object.getOwnPropertyNames(obj)); // Output: []
    console.log(Object.getOwnPropertySymbols(obj)); // Output: [ Symbol(mySymbol) ]
    

Symbols offer a powerful way to create unique property names, especially for scenarios where you want to add properties to objects without interfering with existing properties or unintentional conflicts. By using symbols, you can create truly private properties or define special behaviors in JavaScript objects.

Spread Operator in Object Literals

The spread operator (…) can be used in object literals to specify the properties of the new object.

1
2
3
let x = {a: 1, b: 2};
let y = {b: 3, c: 4};
let z = {...x, ...y}; // => {a: 1, b: 3, c: 4}

Shorthand methods

If you define a method using the new shorthand syntax, the object literal can’t have a property with the same name as the method.

1
2
3
4
5
6
let square = {
    area() { return this.side * this.side; },
    side: 10
};

square.area() // => 100

Getters and Setters

The get and set keywords can be used to define a getter or setter method (or both) in an object literal.

1
2
3
4
5
6
7
8
let o = {
    // An ordinary data property
    dataProp: value,

    // An accessor property defined as a pair of functions
    get accessorProp() { /* function body here */ },
    set accessorProp(value) { /* function body here */ }
};

In JavaScript, getter and setter properties provide a way to define custom accessors for object properties. They allow you to control how the properties are accessed and modified, enabling you to add additional logic or validations. Here’s a quick overview of getter and setter properties:

  1. Getter Properties:
    • Getter properties are defined using the get keyword followed by the property name.
    • They are used to retrieve the value of the property dynamically when accessed.
    • Getter properties do not have an associated value but execute a function that returns the computed value.
1
2
3
4
5
6
7
8
   const obj = {
     get myProperty() {
       // Logic to compute and return the property value
       return someValue;
     }
   };

   console.log(obj.myProperty); // Accessing the getter property
  1. Setter Properties:
    • Setter properties are defined using the set keyword followed by the property name.
    • They are used to modify the value of the property when assigned.
    • Setter properties receive the new value as a parameter and can perform validations or trigger other actions.
1
2
3
4
5
6
7
8
   const obj = {
     set myProperty(newValue) {
       // Logic to validate or process the new value
       someValue = newValue;
     }
   };

   obj.myProperty = 'New Value'; // Assigning a new value to the setter property
  1. Using Getter and Setter Properties:
    • Getter and setter properties can be used together to create a complete encapsulation of property access and modification.
    • They provide control and flexibility over how properties are accessed and modified.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
   const obj = {
     _myProperty: '', // Private backing property

     get myProperty() {
       // Getter logic
       return this._myProperty;
     },

     set myProperty(newValue) {
       // Setter logic
       // Additional validations or actions can be performed here
       this._myProperty = newValue;
     }
   };

   obj.myProperty = 'New Value'; // Using the setter property
   console.log(obj.myProperty); // Using the getter property

Made with ❤️ by Francis K.

This post is licensed under CC BY 4.0 by the author.