JavaScript: Object Modelling with Behavior Delegation

Gene Kuo
7 min readAug 25, 2019

In JavaScript, there are no abstract blueprints for objects called “classes” as there are in traditional object-oriented languages. However, the JavaScript’s [[prototype]] mechanism we will discuss in this article provides behavior delegation for modelling objects in an application, such as the emulation of inheritance, polymorphism, instance construction and so on. Behavior delegation has more flexible and dynamic features than those of class-based object modelling in traditional object-oriented languages.

Traditional Object-oriented Approach to Object Modeling

Many languages provides syntax which enables class-oriented software design, that is, we design parent and child classes as blueprints for objects (or instances). With traditional object-oriented language like Java, objects are instantiated by a mechanism of copying behavior from classes (as templates) to instances. When classes are inherited, this copying also occurs from parent classes to child classes. For polymorphism, methods at different levels of an inheritance relationship looks like referential links from child to parent classes (such as super ), but it is actually the copy mechanism in action from parent to child classes. So, the common scenario of object modelling in most traditional object-oriented languages goes like that: we define a parent class with shared behavior (methods) by analyzing the problem domain. Then, we can define child classes inherited from the parent class by copying mechanism underneath. Each child class can optionally add specialized behavior (override) to handle specific requirements related only to itself. The following figure illustrates the traditional object-oriented approach of modelling and design:

JavaScript Prototype Mechanism

JavaScript’s prototype mechanism is an internal link (a property named [[Prototype]] ) that exists on one object which references another object. When a property/method reference is requested against an object, and no such property/method exists, the internal[[Prototype]] linkage in turn requests the JavaScript engine to look for the property/method on the linked object. This is because the default [[Get]] operation follows the [[Prototype]] link of the object to reach the requested property if it cannot find the requested property on the object directly.

This linkage between objects is called the prototype chain. And, this prototype mechanism is what we can leverage in object modeling in JavaScript.

Since Object.create(..) creates a new object, and links that new object's internal [[Prototype]] to the object you specify, we can create a [[Prototype]] linkage between two objects using the following code.

const obj1 = {
a: 5
};
// create an object linked to obj1
const obj2 = Object.create( obj1 );
myObject.a; // 5

Emulating Classes using Functions

All functions in JavaScript by default get a property on them called prototype , which points to an arbitrary object, for the example below, User.prototype object. And, when a function is called with new , an object is created and it gets an internal [[prototype]] link to the object that the function’s prototype is pointing at. By default, User.prototype gets a public, non-enumerable property called constructor , which is a reference to the function itself, User in this case. However, the object user1 or user2 created by the call new User() does not mean it was constructed by User constructor (just a side-effect of the new call), even user1.constructor === User is true. user1 does not have a constructor property pointing at User, instead user1.constructor points at User via [[prototype]]mechanism as described previously.

function User(name) {
this.name = name;
}
User.prototype.myName = function() {
return this.name;
}
const user1 = new User("a");
const user2 = new User("b");
Object.getPrototypeOf(user1) === User.prototype; //true
User.prototype.constructor === User; // true
user1.constructor === User; // true
user1.myName(); // a
user2.myName(); // b

In traditional object-oriented languages, the process of instantiating (or inheriting from) a class exercises the mechanism of copying the defined behavior from that class into a physical object to create each new instance. However, as we can see in JavaScript, we don’t create multiple instances of a class. Instead, we create multiple objects (such as const user1 = )that [[Prototype]] link to a common object.

Trying to emulate class-oriented design with JavaScript as those in traditional object-oriented languages, such as classes, constructors, new, copy operations, inheritance, and polymorphism, will often lead to much confusion about how JavaScript actually works. Capitalized-named functions called with new to create a JavaScript object does not mean the function is a constructor and its name is the name of a class. Functions are just plain normal functions, not constructors, and there are no copy operations from classes to instances when using new on functions. There are only objects [[prototype]] linked to other objects which the property and function access can be delegated to.

Sonew User() is the implicit way to create a new object linked to another object, and there are another way, Object.create(..) , is the explicit way to create a new object linked to another object.

Prototypal Inheritance

There is no real “classical inheritance” in JavaScript since inheritance implies a copy operation, and JavaScript doesn’t use the copy operation and instead uses prototype mechanism to create links between two objects, which allows one object to delegate property and function access to another object.

Based on prototype mechanism, we can say that user1 can have [[prototype]] link to User.prototype for property and function delegation. And, we can also form the concept of a parent-child “class” inheritance, such as linkage fromEmployee.prototype “object” to User.prototype “object”, to resemble the class inheritance in traditional object-oriented languages.

The following code listing illustrates the prototype mechanism in action, and the resemblance of class-inheritance in traditional object-oriented languages.

function User(name) {
this.name = name;
}

User.prototype.getName = function() {
return this.name;
};

function Employee(name, position) {
User.call(this, name);
this.position = position;
}
// Make a new Employee.prototype linked to User.prototype
// Employee.prototype.constructor is gone
Employee.prototype = Object.create(User.prototype);
Employee.prototype.myPosition = function() {
return this.position;
};

const john= new Employee( "John", "Developer" );

john.myName(); // "John"
john.myPosition(); // "Developer"

ES6 Classes

With the introduction of ES6's class and super, which provides a syntactic sugar to eliminate the ugliness of various prototype related calls seen previously, and to emulate traditional object-oriented class related concepts and syntax; since those are not real classes, and they still base on the [[prototype]] mechanism we discuss earlier. We, as developers, have a choice to how to design an object model based on ES6 syntactic sugar, or to design it based on behavior delegation or mixins more flexibly.

class User {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
class Employee extends User {
constructor(name, position) {
super(name);
this.position = position;
}
getName() {
return super.getName();
}
getPosition() {
return this.position;
}
}
const john = new Employee(“John”, “Developer”);
john.getName(); // "John"
john.getPosition(); // "Developer"

Behavior Delegation

Behavior Delegation leverages JavaScript’s prototype mechanism (linking objects to other objects) which lets some object provide a delegation to common utility objects for property or method references if not found on the object itself. This is very different from the traditional object-oriented design pattern. There are no parent and child classes, inheritance, polymorphism, etc. We can view objects in JavaScript as horizontally peered objects and they can be linked side-by-side to from the delegation linkage as necessary. Unlike the traditional object-oriented languages, there are no complex inheritance levels to consider and no copying mechanism happening.

const User = {
init(name) {
this.name = name;
},
getName() {
return this.name;
}
};
const Employee = Object.create(User);Employee.build = function(name, position) {
this.init(name);
this.position = position;
};
Employee.outputDetails = function() {
console.log(this.getName() + " is a " + this.position);
};
const john = Object.create(Employee);
john.build("John", "Developer");
const mary = Object.create(Employee);
mary.build("Mary", "Manager");
john.outputDetails(); // John is a Developer
mary.outputDetails(); // Mary is a Manager

In the following listings, we will use more concrete example for illustrating behavior delegation in JavaScript, focusing on simple web UI design.

We first must load jQuery into our Browser’s (Chrome, for example) developer tool.

let script = document.createElement("script");
script.setAttribute('src', 'https://code.jquery.com/jquery- latest.min.js');
script.addEventListener('load', function() {
let script = document.createElement('script');
document.body.appendChild(script);
}, false);
document.body.appendChild(script);

Then, we declare a variable Widget pointing to an object with init() and insert() utilities for other objects to link and delegate behavior to.

const Widget = {
init(width,height){
this.width = width || 50;
this.height = height || 50;
this.$elem = null;
},
insert($where){
if (this.$elem) {
this.$elem.css({
width: this.width + "px",
height: this.height + "px"
}).appendTo($where);
}
}
};

The Button object we define here can be independent from the previousWidget object. They are now just peer-to-peer objects.

const Button = {
setup(width,height,label){
// delegated call
this.init(width, height);
this.label = label || "Default";
this.$elem = $("<button>").text(this.label);
},
build($where) {
// delegated call
this.insert($where);
this.$elem.click(this.onClick.bind(this));
},
onClick(evt) {
console.log("Button '" + this.label + "' clicked!");
}
};

Now, we can set the prototype (i.e., the internal [[Prototype]] property) of Button to a new prototype, which is Widget.

Object.setPrototypeOf(Button, Widget);

Finally, we can test it on a browser to see behavior delegation in action.

$(document).ready(function(){
const $body = $(document.body);
const btn1 = Object.create(Button);
btn1.setup(120, 30, "A");
const btn2 = Object.create(Button);
btn2.setup(150, 50, "B");
btn1.build($body);
btn2.build($body);
} );

Conclusions

In JavaScript, the [[Prototype]] mechanism links objects to other objects. There are no abstract mechanisms like "classes", no matter how much we try to convince ourselves otherwise.

As we can see, behavior delegation is the more natural way to model objects in JavaScript. It leverages the prototype lookup process to enable peered objects to delegate behavior with each other without strictly declaring class hierarchy or tightly-coupled composition, like in traditional object-oriented languages.

Thanks for reading.

--

--

Gene Kuo

Solutions Architect, AWS CSAA/CDA: microservices, kubernetes, algorithms, Java, Rust, Golang, React, JavaScript…