JavaScript ES2015 Classes and Properties (Part 2 of 2)

February 11, 2016 in JavaScript Articles

Written by Eric Greene


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.


Written by Eric Greene

Eric Greene

Eric is a professional software developer specializing in HTML, CSS, and JavaScript technologies. He has been developing software and delivering training classes for nearly 19 years. He holds the MCSD Certification for ASP.Net Web Applications, and is a Microsoft Certified Trainer.



Contact Us:

Accelebrate’s training classes are available for private groups of 3 or more people at your site or online anywhere worldwide.

Don't settle for a "one size fits all" public class! Have Accelebrate deliver exactly the training you want, privately at your site or online, for less than the cost of a public class.

For pricing and to learn more, please contact us.

Contact Us Train For Us

Toll-free in US/Canada:
877 849 1850
International:
+1 678 648 3113

Toll-free in US/Canada:
866 566 1228
International:
+1 404 420 2491

925B Peachtree Street, NE
PMB 378
Atlanta, GA 30309-3918
USA

Subscribe to our Newsletter:

Never miss the latest news and information from Accelebrate:

Microsoft Gold Partner

Please see our complete list of
Microsoft Official Courses

Recent Training Locations

Alabama

Huntsville

Montgomery

Birmingham

Alaska

Anchorage

Arizona

Phoenix

Tucson

Arkansas

Fayetteville

Little Rock

California

San Francisco

Oakland

San Jose

Orange County

Los Angeles

Sacramento

San Diego

Colorado

Denver

Boulder

Colorado Springs

Connecticut

Hartford

DC

Washington

Florida

Fort Lauderdale

Miami

Jacksonville

Orlando

Saint Petersburg

Tampa

Georgia

Atlanta

Augusta

Savannah

Idaho

Boise

Illinois

Chicago

Indiana

Indianapolis

Iowa

Ceder Rapids

Des Moines

Kansas

Wichita

Kentucky

Lexington

Louisville

Louisiana

Banton Rouge

New Orleans

Maine

Portland

Maryland

Annapolis

Baltimore

Hagerstown

Frederick

Massachusetts

Springfield

Boston

Cambridge

Michigan

Ann Arbor

Detroit

Grand Rapids

Minnesota

Saint Paul

Minneapolis

Mississippi

Jackson

Missouri

Kansas City

St. Louis

Nebraska

Lincoln

Omaha

Nevada

Reno

Las Vegas

New Jersey

Princeton

New Mexico

Albuquerque

New York

Buffalo

Albany

White Plains

New York City

North Carolina

Charlotte

Durham

Raleigh

Ohio

Canton

Akron

Cincinnati

Cleveland

Columbus

Dayton

Oklahoma

Tulsa

Oklahoma City

Oregon

Portland

Pennsylvania

Pittsburgh

Philadelphia

Rhode Island

Providence

South Carolina

Columbia

Charleston

Spartanburg

Greenville

Tennessee

Memphis

Nashville

Knoxville

Texas

Dallas

El Paso

Houston

San Antonio

Austin

Utah

Salt Lake City

Virginia

Richmond

Alexandria

Arlington

Washington

Tacoma

Seattle

West Virginia

Charleston

Wisconsin

Madison

Milwaukee

Alberta

Edmonton

Calgary

British Columbia

Vancouver

Nova Scotia

Halifax

Ontario

Ottawa

Toronto

Quebec

Montreal

Puerto Rico

San Juan

© 2013-2019 Accelebrate, Inc. All Rights Reserved. All trademarks are owned by their respective owners.