JavaScript Objects

JavaScript Objects

Object oriented programming in JavaScript.

ยท

9 min read

JavaScript objects are composite variables that encapsulate multiple values and methods. Each value is represented by a different element that has a name and is called an "object attribute" or "field". Objects can also contain functions. These functions are called in object-oriented design: "methods" but in JavaScript, they are simple functions that happen to be defined in an object context.

Object Literal

Object literals are using a specific notation invented for JavaScript that is called: "JSON = "Java Script Object Notation". This notation is nice and slim, so it was adopted by many other programming languages as a data format.

A simplified version of JSON has become standard for storing objects in text files or sending objects over the network. It is considered a lightweight data representation format. It cannot transport functions though.

Example:

let person = {
   firstName : "John",
   lastName : "Doe",
   age : 50,
   eyeColor : "blue",
   fullName : function () {
      return this.firstName + " " + this.lastName
   }
};

In this example, we declare an object: person that has several properties and one method. A method is a property of a type function. The method name in this example is fullName.

Observe:

  • The method body is enclosed in brackets {โ€ฆ} and belongs to the person object,

  • Inside methods, you can use the keyword "this" to refer to its current object.

Object Members

You can access object members in two ways:

  • objectName.propertyName

  • objectName["propertyName"]

You can access an object method with the following syntax:

  • objectName.methodName();

Inside methods, you can access the properties of an object using the "this" keyword:

  • this.propertyName;

Also, if you assign a function to a variable by mistake, don't forget to use parentheses () for a function call. Otherwise, you can assign the function itself! The empty parentheses have the role to execute the function and are required even if there are no parameters for the function.

Example:

console.log(person.firstName); // John
console.log(person.lastName); // Doe
console.log(person.fullName());// John Doe

Empty Object

You can create an empty object using the syntax:

// create empty object
const objectName = {};
// add properties
objectName.x = 10;
objectName.y = 20;

Note: This is just a demo. In practice, you should not use this method to create objects. The code looks messy and is considered bad practice.

Object Factory

An object factory is a normal function that creates an object. This kind of function is no longer used in practice but in older JavaScript code. It is good to know about this so you can read older code:

Example:

/* object factory function */
function createNewPerson(name) {
   var obj = {};
   obj.name = name;
   obj.greeting = function() {
      console.log('Hi! I\'m ' + obj.name + '.');
   };
   return obj;
}
/* create new objects */
var johnPerson = createNewPerson("John");
var petruPerson = createNewPerson("Petru");
johnPerson.greeting(); //call object method
petruPerson.greeting(); //call object method

Object Constructor

This is a special function that can produce an object, using the keyword "new". This function is more elegant than "object factory". Does not have return and is used only to create object instances.

Example:

/* object constructor */
function Person(name) {
   this.name = name;
   this.greetings = function() {
   console.log('Hi! I\'m ' + this.name + '.');
 };
};
// instantiate two objects
var john = new Person("John");
var petru = new Person("Petru");
john.greetings(); // Hi! I'm John
petru.greetings();// Hi! I'm Petru

Fundamental Object

JavaScript has predefined objects. One of these is the "Fundamental Object". There are two alternative methods to create objects using the fundamental object:

  • By using the fundamental constructor: new Object()

  • By reusing an existing object using method: Object.create()

Example:

// equivalent to "var o = {}";
let o = new Object();
// equivalent to "b = new Boolean()";
let b = new Object(Boolean());

Object Prototype

Object.create() method can be used to create a new object from an existing object. The new object is based on an "object prototype". An object prototype is automatically created when you create the object.

By reusing a prototype, you inherit all its methods. This is how inheritance works in JavaScript. Therefore JavaScript is a Prototype Oriented Language. You can access the prototype using dot notation and you can extend the prototype with new functions and properties.

Example:

// object constructor Shape:
function Shape() {
   this.name = 'Shape';
}
// object constructor Rectangle:
function Rectangle() {
   /* Using JavaScript special function call(),
      an object can use a method belonging to another object */
   Shape.call(this);
}
// binding prototype, to realize the inheritance
Rectangle.prototype = Object.create(Shape.prototype);
// test inheritance
const rect = new Rectangle();
console.log(rect.name); // expected output: "Shape"

How to Modify Prototype

The prototype of an object can be modified. You can add new functions and properties to a prototype using dot notation. When you do, all instances of the prototype will receive new members.

/* define constructor for Person */
function Person(name) {
   /* property and method definitions */
   this.name = name;
   /* public method: hello */
   this.hello = function() {
      console.log("hello " + this.name);
   }
};
/* create an object */
let person = new Person('Smith');
/* extend Person with a new function */
Person.prototype.farewell = function() {
   console.log("By "+ this.name);
};
person.hello(); // test internal method
person.farewell(); // test an external method

Function Object

Every JavaScript function is actually a Function object. It can be created with the alternative notation:

/* alternative notation for function object */
let sum = new Function('a', 'b', 'return a + b');
console.log(sum(2, 6)); // expected output: 8

The example above is only to prove a point. It is not used in practice. The point is: if a function is an object, it can have properties. It means you can create new properties for a function using dot notation. Function Properties

Having a property for a function is similar to a static variable. It can take values that are visible from outside the function and can be modified to modify the function behavior.

Example:

/* function with static property */
function getNext() {
    if (getNext.next == undefined) {
       getNext.next = 0;
    }
    else {
       getNext.next++;
    }
    return getNext.next;
}
/* test static property */
console.log(getNext()); // 0
console.log(getNext()); // 1
getNext.next = 8;
console.log(getNext()); // 8
getNext.next = 12;
console.log(getNext()); // 13

Observe: You must use: function.property to define function properties. They are attached to the function itself, not to an object. You cannot use this.property to define function properties. That is the difference.

Define classes

Is your head spinning yet? Sorry, this prototype theory proves to be difficult to grasp and sometimes insufficient. Therefore JavaScript tries to evolve and has introduced a new concept of "class". This is an attempt to adopt Java-style OOP. In fact, a "class" is syntax sugar for a constructor function. It can be used to create object instances using the keyword: "new".

Syntax:

class MyClass {
   property_name = value; // class property
   constructor(...) {    // constructor has always this name
      // ...
   }
   method_name(...) {}    // class method
   second_method(...) {} // class method
   get something(...) {}  // getter method
   set something(...) {}  // setter method
}

Example:

In this example, you can see 2 classes. One is extending the other. This is how inheritance can be done using the new JavaScript notation.

// base class
class Polygon {
   constructor(width, height) {
      this.name = "Polygon";
      this.width = width;
      this.height = height;
   }
}
// instantiate object: polygon
let polygon = new Polygon(20, 30);
console.log(polygon.name); // "Polygon"

// create a child class
class Square extends Polygon {
   constructor(length) {
      /* call constructor
         of the parent class
         this is a mandatory call */
      super(length, length);
      /* set initial value */
      this.name = 'Square';
   }

   // read-only property
   get area() {
      return this.height * this.width;
   }
};

// test new class: Square
let square = new Square(20);
console.log(square.name); // Square
console.log(square.area); // 400
square.area = 50; // no effect
console.log(square.area); // 400

Notes:

  • Observe: area() it is, in fact, a pseudo-property, not a method!?

  • Classes are not yet supported for all browsers and JavaScript engines,

  • Class properties are a draft proposal not yet supported in all engines.

Object Destructuring

You can use destructuring assignments with object literals. This is especially interesting when a function returns multiple results that need to be assigned to different variables. The destructor will read attributes by name and will distribute them into independent variables. JavaScript is weird when it does this. If you are not aware, you will not understand the code.

Example:

/* declare and initialize object */
let obj = {a: 42, b: true, c:"test"};
/* partial deconstructing object */
let {a, c} = obj;
console.log(a); // 42
console.log(c); // test
/* partial decosntructing one attribute */
let {b} = obj;
console.log(b); // true

Observe: Object deconstruction is very different than array deconstruction. It works by name, not by position. In the example above, attribute "b" is not captured into a variable but skipped. We read only attributes "a" and "c". Therefore c == "test". In the end, we read also "b" using the destructor.

/* object with 2 attributes */
let o = {p: 42, q: true};
/* mapping attributes */
let {p: foo, q: bar} = o;
console.log(foo); // 42
console.log(bar); // true

Best Practices:

  • Always initialize properties when creating objects.

  • Avoid using delete operator on objects as it can cause performance degradation.

  • Use dot notation when possible to access object properties and methods.

  • Use objects to organize functions and data together.

  • Use constructors to create objects with common properties and methods.

  • Use Object.assign() to clone objects and avoid mutation.

Common Errors and Pitfalls:

  • Trying to access a property or method of an undefined object can cause an error.

  • Forgetting to use the new keyword when creating object instances using a constructor function.

  • Using comparison operators to compare two objects can lead to unexpected results.

  • Forgetting to use this keyword inside object methods.

  • Not being careful with object cloning can lead to bugs related to object mutation.


Conclusion

Classes and Object-Oriented Programming (OOP) in JavaScript have become extremely popular in recent times. With the introduction of ES6, it became much easier to implement OOP concepts using classes in JavaScript.

Classes are a template for creating objects that encapsulate data with associated behaviors. This provides an elegant and intuitive way for modeling real-world situations as it allows us to create reusable code, organize functionality, and create a clear separation of concerns.

JavaScript classes give developers access to a range of coding paradigms like inheritance, polymorphism, encapsulation, and abstraction, that enable developers to write modular and clean code.

However, while classes and OOP provide a lot of benefits, they can also make code hard to follow if used incorrectly. Nested inheritance or overuse of inheritance, excessive use of getters and setters, and tight coupling can lead to complex and hard-to-maintain code.

In conclusion, Classes and OOP in JavaScript are powerful tools for creating maintainable and reusable code. However, developers should learn and apply OOP design patterns and best practices to avoid common pitfalls and keep the codebase clean and easy to maintain.


Disclaim: This article was created with collaboration between humans and machines. If you find errors, comment below. I will try to fix them. Otherwise, enjoy the know-how and move on. There are more articles to read.


Learn and prosper. ๐Ÿ––

ย