SHARE
Facebook X Pinterest WhatsApp

Duck Typing in JavaScript

Written By
thumbnail
Rob Gravelle
Rob Gravelle
May 21, 2018

In the Monkey Patching article, I introduced the term “Duck Typing.” Coined by Ruby coders, Duck Typing is based on the principle, “If it walks like a duck and it quacks like a duck, then it must be a duck.” Following that precept, rather than attempt to determine an object’s suitability for a given purpose according to its type, in duck typing, an object’s suitability is determined by the presence of certain methods and properties. Once you get familiar with the concept, you begin to notice where Duck Typing has been employed in many programming languages — including JavaScript — with great success. In today’s article, we’ll trace its evolution in JavaScript and explore some situations where we can apply Duck Typing for our own objectives.

Duck Typing Roots in JavaScript

Once upon a time, script writers employed a technique called Browser Sniffing — a.k.a user-agent sniffing — to determine whether to code in JavaScript (Netscape) or JScript (Internet Explorer). Over time, this practice was eclipsed by Feature Detection, whereby a specific feature/capability was targeted instead. For example:

//Browser Sniffing
if (navigator.userAgent.indexOf("MSIE 7") > -1){
    //do something
}


//Feature Detection
if(document.all){
    //do something
}

Feature Detection was a notable improvement over Browser Sniffing, but unfortunately, a lot of web developers went a step further by using the document.all test as an implicit check for Internet Explorer. Assuming that the browser is in fact IE, coders would then proceed with erroneous assumption that it was also safe to use other IE-specific methods and properties like document.uniqueID property. In Duck Typing, one would not make any such assumptions based on any particular test.

Later, several shim libraries were developed to detect HTML5 and CSS3 features in various browsers in a standardized manner. The more well known of these is Modernizr. Created by a few developers, including Paul Irish, Modernizr has won several awards. It creates a global Modernizr object that contains a set of boolean properties for each feature it can detect. For example, if a browser supports the canvas API, the Modernizr.canvas property will be set to true, or false otherwise:

if (Modernizr.canvas) {
  // let's draw some shapes…!
} else {
  // no native canvas support available 🙁
}

Unlike the earlier Feature Detection for browsers, shims like Modernizr restrict their feature checks to individual properties.

Emulating Interfaces with Duck Typing

Interfaces are one of the most powerful features of programming languages like Java. It’s a file that contains a collection of function signatures. When an object implements the interface, it is symbolically signing a contract whereby it agrees to perform the specific behaviors (functions) of the interface. If an object fails to live up to its side of the bargain, your program won’t even compile!

While JavaScript is not an Object-Oriented language like Java (it uses Prototypal inheritance), there are ways to emulate Interface-like features. Let’s see how.

For our demo, let’s create three types of cars:

function Infiniti(model) { 
    this.model = model;
}

function Honda(model) {
    this.model = model;
}

function Hyundai(model) {
    this.model = model;
}

We can instantiate each Car type using the new keyword, passing in its type:

var cars = [
  new Infiniti("G37"),
  new Honda("Civic"),
  new Hyundai("Accent")
];

Suppose we wanted to race our cars. We could code something like this, where the accelerating prowess of each car is described:

function race(car) {
    if (car instanceof Infiniti) {
        console.log(car.model + ": Going from 0 to 60mph really fast!");
    } else if (car instanceof Honda) {
        console.log(car.model + ": Going from 0 to 60mph at a moderate speed.");
    } else if (car instanceof Hyundai) {
        console.log(car.model + ": Going from 0 to 60mph at a crawl.");
    }
}

//Let's race some cars!
for (var i in cars) { 
 race(cars[i]);
}

/* outputs:
   "G37: Going from 0 to 60mph really fast!"
   "Civic: Going from 0 to 60mph at a moderate speed."
   "Accent: Going from 0 to 60mph at a crawl."* 
*/

*No disrespect to owners of Hyundai Accents. I’m merely poking fun at a buddy of mine!

The instanceof operator tests for specific Car types and outputs text accordingly.

Although there is nothing technically wrong with the above code, there is a fairly serious design flaw, and that is that every time we create a new Car type, we have to update the race() function!

Needless to say, there is a better way to tailor the race() function for a variety of Car types.

Let’s imagine that car types must implement an accelerate() method that is specific to that particular Car type. Then, in the race() function, we can simply pass the Car instance to the race() function so that it may in turn invoke the object’s accelerate() method:

function Infiniti(model) { 
    this.model = model;
}
Infiniti.prototype.accelerate = function() {
    console.log(this.model + ": Going from 0 to 60mph really fast!");
}

function Honda(model) {
    this.model = model;
}
Honda.prototype.accelerate = function() {
    console.log(this.model + ": Going from 0 to 60mph at a moderate speed.");
}

function Hyundai(model) {
    this.model = model;
}
Hyundai.prototype.accelerate = function() {
    console.log(this.model + ": Going from 0 to 60mph at a crawl.");
}

function race(car) {
    //Dynamically invoke accelerate() method 
    //of whatever car was passed in.
    car.accelerate(); 
}

//Let's race some cars!
var cars = [
  new Infiniti("G37"),
  new Honda("Civic"),
  new Hyundai("Accent")
];
for (var i in cars) { 
 race(cars[i]);
}

Guarding Against Impostors

Since JavaScript lacks the compiler checking of a programming languages such as Java, it still wouldn’t hurt to check that the passed object can in fact accelerate and throw an error if it cannot. To illustrate, we’ll pass a couch to the race() function:

function race(car) {
  //Dynamically invoke accelerate() method 
  //of whatever car was passed in.
  if (car.accelerate) 
    car.accelerate(); 
  else 
    throw new DuckTypeError("Hey, you're not a car!");
}

//try to race a sofa!?
function Couch(type) {
    this.type = type;
}
race(new Couch("Sofa")); 

Here is the output produced when trying to race the Couch. Note that the DuckTypeError code is not shown here, but it is in the demo:

G37: Going from 0 to 60mph really fast!
Civic: Going from 0 to 60mph at a moderate speed.
Accent: Going from 0 to 60mph at a crawl.
SCRIPT5022: DuckTypeError: Hey, you're not a car!
index.html (71,7)

Duck Typing makes sure that our object “quacks like a duck.” The advantage to this approach is that we don’t have to make any further updates the the race() function to accommodate new Car types.

Conclusion

In today’s article, we traced the evolution of Duck Typing in JavaScript and explored how to utilize Duck Typing to emulate the behavior of Java Interfaces in JS objects. There is a full demo on Codepen for you to explore.

Recommended for you...

The Revolutionary ES6 Rest and Spread Operators
Rob Gravelle
Aug 23, 2022
Ahead of Time (AOT) Compilation in Angular
Tariq Siddiqui
Aug 16, 2022
Converting a JavaScript Object to a String
Rob Gravelle
Aug 14, 2022
Understanding Primitive Type Coercion in JavaScript
Rob Gravelle
Jul 28, 2022
HTML Goodies Logo

The original home of HTML tutorials. HTMLGoodies is a website dedicated to publishing tutorials that cover every aspect of being a web developer. We cover programming and web development tutorials on languages and technologies such as HTML, JavaScript, and CSS. In addition, our articles cover web frameworks like Angular and React.JS, as well as popular Content Management Systems (CMS) that include WordPress, Drupal, and Joomla. Website development platforms like Shopify, Squarespace, and Wix are also featured. Topics related to solid web design and Internet Marketing also find a home on HTMLGoodies, as we discuss UX/UI Design, Search Engine Optimization (SEO), and web dev best practices.

Property of TechnologyAdvice. © 2025 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.