US & Canada: 877 849 1850
International: +1 678 648 3113

Accelebrate Blog

ACCELERATED LEARNING, CELEBRATED RESULTS

JavaScript ES2015 Classes and Properties (Part 2 of 2)

Classes, Objects and Properties

In the previous post, JavaScript ES2015 Classes and Prototype Inheritance (Part 1 of 2), we explored the new class syntax and the many intricacies of prototype inheritance.

In this post, we will continue to explore properties that can be configured with the new class syntax and made available on objects that are instantiated. In particular, we will explore function properties, accessor properties, and static properties. The sharing of functions with multiple objects is the primary purpose of prototype inheritance ; therefore, understanding how to configure functions on classes is important.

Accessor properties have been around since the days of ECMAScript 5 (ES5), yet many developers do not know they even exist in the language. We will examine the new class syntax which makes them easier to work with.

Finally, many programming languages offer static members on class definitions. Static members are directly available on the class, with no specific instance required. Such properties are commonly used for helper functions related to the class. Even though there is no concept of static properties in JavaScript, the new class syntax offers a kind of static property through the static keyword.  We will dive deeper into those as well. Use of static variables in certain cases was originally popularized by Java.  You can read an excellent tutorial on this concept at https://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html.

Function Properties

In practice, the primary purpose of prototype inheritance is to allow the inheritance of function properties (properties which point to any kind of function) from the parent object to the child object. Generally, value properties should exist directly on the child object because values are typically specific to a particular child object. Additionally, traversing the prototype chain to retrieve an object’s values is inefficient. However, function properties are different from value properties. Rarely are function implementations specific to a particular object, therefore, sharing the same function object between multiple objects reduces memory consumption by the JavaScript application.

The key to different objects sharing the same function object is that the context of function execution is always determined by how the function is called, not the lexical position of the function in the source code. This flexibility means the same function can be used with any object, and the value of this within the function will always refer to the object which qualified the function call.

// 'getFullName' function object
function getFullName() {
    return this.firstName + " " + this.lastName;
}

var person1 = {
  firstName: "Bob",
  lastName: "Smith",
  // property named 'getFullName' referencing the 'getFullName' function object above
  getFullName: getFullName
};

var person2 = {
  firstName: "Tim",
  lastName: "Johnson",
  // property named 'getFullName' referencing the 'getFullName' function object above
  getFullName: getFullName
};

// outputs "Bob Smith"
console.log(person1.getFullName());

// outputs "Tim   Johnson"
console.log(person2.getFullName());

// outputs "true" because both properties point to the same function
console.log(person1.getFullName === person2.getFullName);

Click Here to Download the Code [this.js]

The property ‘getFullName’ on both the person1 and person2 objects points to the same ‘getFullName’ function object. By reusing the same function object for both objects (and the number of objects is not limited), less memory is used while allowing the function to be shared between different objects.

This concept, combined with prototype inheritance, allows for a function object to be defined on the prototype object.  All objects inheriting from the prototype can use the same function with the value of this for each function call to be specific to the object against which the function is being executed.

To define properties to be inherited from the prototype, the following syntax is used.

"use strict";

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  // function definition
  // a property referencing the function will exist on
  // the prototype object, not on each instance of Person
  getFullName() {
    return this.firstName + " " +   this.lastName;
  }
}

var person = new Person("Bob", "Smith");
// invoke the function on the instance
// the function is referenced on the prototype
// to find the function, the prototype chain is
// traversed, then function is executed within
// the context of the 'person' instance
console.log(person.getFullName());

Click Here to Download the Code [functions.js]

Observe how the function object ‘getFullName’ is defined without using the function keyword. With the ES2015 class syntax, the keyword function is omitted from the function object definitions.  This function will not be a direct property of the person object; rather, it will be on the Person prototype that the person object will inherit from.

Getter/Setter Properties

Getter and setter properties are not new to JavaScript, even though few JavaScript developers know that they exist or how to use them.  With the addition of property descriptors in ES5, object properties could be defined with property descriptors to be either standard value properties or to use accessor functions for getting and setting the values of properties. Developers from a .NET background are familiar with the accessor pattern for properties since .NET uses accessors for properties in .NET class definitions.

"use strict";
class Person {

constructor(firstName, lastName) {
  // internal properties can be prefixed with an underscore
  this._firstName = firstName;
  this._lastName = lastName;
}

// the name of the property is used when defining the accessor functions
get firstName() {
  // return the value of the internal property,
  // other logic can be performed here as well
  // internal properties are a convention, not
  // part of the JavaScript languagereturn this._firstName;
}

// the accessor functions are defined on the prototype, while
// the values are stored on internal properties on the instance
set firstName(value) {
  // set the value of the internal property,
  // validation or other logic can go here as well
  this._firstName = value;
  }
}

var person = new Person("Bob", "Smith");

// properties are set like normal value properties, the accessors are implicitly called
person.firstName = "Tommy";

// get operations work in a similar fashion
console.log(person.firstName);

Click Here to Download the Code [accessors.js]

Static Properties

As first mentioned in the extends function source code comments above, JavaScript provides a convention for defining static properties for objects.  ES2015 classes continue this convention through the formalized keyword static.

"use strict";

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  //   static function definition
  // not accessible on the instance
  // function is defined on the class (function) object
  // itself, not the prototype; therefore, its not
  // inherited and cannot be shadowed (overridden)
  // by a child object
  static getFormattedName(person) {
    return person.lastName + ", " + person.firstName;
  }
}

var person = new Person("Bob", "Smith"); // reference the static function directly on the class (function) object
console.log(Person.getFormattedName(person));

Static function are defined directly on the class itself, and are not accessible using the this object. Static functions are primarily used as helper functions related to the class.

Click Here to Download the Code [static.js]

Private Properties

ES2015 still does not formally support the use of public, protected, and private access modifiers for object properties.  With JavaScript, all properties are public.  Some preprocessors such as TypeScript added transpile time support for public and private access modifiers.  With a little JavaScript wizardry using class expressions and closures, private properties can be achieved.

"use strict";

var Person = {
  // a wrapper function is needed to create new private variable for each
  // instance of the class
  // the class definition source code my be defined in the wrapper function
  // because closures dependent the lexical structure of the code
  create: function(...cargs) {
    let _name = undefined;
    return new class {
      constructor(name) {
        _name = name;
      }
      get name() {
        return _name;
      }
      set name(value) {
        _name = value;
      }
    }(...cargs);
  }
};

// call the wrapper function to   create the object
var person =   Person.create("Bob Smith");
console.log(person.name);

Click Here to Download the Code [private.js]

While this will work, it has some downsides. While closures are used all of the time in JavaScript, they can hurt application performance and consume extra memory. Creating more closures to simply hide some properties is not generally worth it. Also, a new class definition object is created with each call to the wrapper function. The class definition must be defined in the wrapper function to leverage closures. The creation of this class definition over and over will also consume more memory and be inefficient.

It is key to remember that JavaScript is not C++, Java, or C#. It does not support classical inheritance and is not currently designed to mimic the typical features of classical inheritance such as access modifiers. Prefixing private (better termed internal) properties with an underscore or other character (Angular.js uses a $) is a commonly accepted pattern, and achieves the same goal without incurring the performance and memory overhead.

Conclusion

By adopting and formalizing many current JavaScript object design patterns, ES2015 classes greatly improves the syntax to define object inheritance, getter/setter properties. It also clarifies the differences between which properties are defined on the object instance, which are inherited, and which once are considered static. While ES2015 classes do not change the nature of prototype inheritance, they do make prototype inheritance more accessible to JavaScript developers. Hopefully this new syntax will allow new and old JavaScript developers alike to further expand their understanding and use of prototype inheritance in JavaScript.

JavaScript ES2015 Classes and Prototype Inheritance (Part 1 of 2)


Author: Eric Greene, one of Accelebrate’s instructors.

Accelebrate offers private AngularJS training and JavaScript training for groups and instructor-led online JavaScript classes for individuals.

Categories: JavaScript Articles
Tags: , ,

Leave a Reply

Your email address will not be published. Required fields are marked *

Your email address will not be published. Required fields are marked *

*



You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Please contact us for GSA pricing.
Contract #GS-35F-0307T

Please see our complete list of
Microsoft Official Courses