• Skip to main content
  • Select language
  • Skip to search
MDN Web Docs
  • Technologies
    • HTML
    • CSS
    • JavaScript
    • Graphics
    • HTTP
    • APIs / DOM
    • WebExtensions
    • MathML
  • References & Guides
    • Learn web development
    • Tutorials
    • References
    • Developer Guides
    • Accessibility
    • Game development
    • ...more docs
Add-ons
  1. MDN
  2. Mozilla
  3. Add-ons
  4. Add-on SDK
  5. Guides
  6. Contributor's Guide
  7. Classes and Inheritance

Classes and Inheritance

In This Article
  1. Constructors
  2. Prototypes
  3. Inheritance and Constructors
  4. Inheritance and Prototypes
  5. Inheritance and Instanceof
  6. Overriding Methods
  7. Classes in the Add-on SDK

Add-ons using the techniques described in this document are considered a legacy technology in Firefox. Don't use these techniques to develop new add-ons. Use WebExtensions instead. If you maintain an add-on which uses the techniques described here, consider migrating it to use WebExtensions.

From Firefox 53 onwards, no new legacy add-ons will be accepted on addons.mozilla.org (AMO).

From Firefox 57 onwards, WebExtensions will be the only supported extension type, and Firefox will not load other types.

Even before Firefox 57, changes coming up in the Firefox platform will break many legacy extensions. These changes include multiprocess Firefox (e10s), sandboxing, and multiple content processes. Legacy extensions that are affected by these changes should migrate to WebExtensions if they can. See the "Compatibility Milestones" document for more.

A wiki page containing resources, migration paths, office hours, and more, is available to help developers transition to the new technologies.

A class is a blueprint from which individual objects are created. These individual objects are the instances of the class. Each class defines one or more members, which are initialized to a given value when the class is instantiated. Data members are properties that allow each instance to have their own state, whereas member functions are properties that allow instances to have behavior. Inheritance allows classes to inherit state and behavior from an existing classes, known as the base class. Unlike languages like C++ and Java, JavaScript does not have native support for classical inheritance. Instead, it uses something called prototypal inheritance. As it turns out, it is possible to emulate classical inheritance using prototypal inheritance, but not without writing a significant amount of boilerplate code.

Classes in JavaScript are defined using constructor functions. Each constructor function has an associated object, known as its prototype, which is shared between all instances of that class. We will show how to define classes using constructors, and how to use prototypes to efficiently define member functions on each instance. Classical inheritance can be implemented in JavaScript using constructors and prototypes. We will show how to make inheritance work correctly with respect to constructors, prototypes, and the instanceof operator, and how to override methods in subclasses. The SDK uses a special constructor internally, known as Class, to create constructors that behave properly with respect to inheritance. The last section shows how to work with the Class constructor. It is possible to read this section on its own. However, to fully appreciate how Class works, and the problem it is supposed to solve, it is recommended that you read the entire article.

Constructors

Before the ECMASCript 6 standard, a JavaScript class was defined by creating a constructor function for that class. To illustrate this, let's define a simple constructor for a class Shape:

function Shape(x, y) {
    this.x = x;
    this.y = y;
}

We can now use this constructor to create instances of Shape:

let shape = new Shape(2, 3);
shape instanceof Shape; // => true
shape.x; // => 2
shape.y; // => 3

The keyword new tells JavaScript that we are performing a constructor call. Constructor calls differ from ordinary function calls in that JavaScript automatically creates a new object and binds it to the keyword this for the duration of the call. Moreover, if the constructor does not return a value, the result of the call defaults to the value of this. Constructors are just ordinary functions, however, so it is perfectly legal to perform ordinary function calls on them. In fact, some people (including the Add-on SDK team) prefer to use constructors this way. However, since the value of this is undefined for ordinary function calls, we need to add some boilerplate code to convert them to constructor calls:

function Shape(x, y) {
    if (!this)
        return new Shape(x, y);
    this.x = x;
    this.y = y;
}

Prototypes

Every object has an implicit property, known as its prototype. When JavaScript looks for a property, it first looks for it in the object itself. If it cannot find the property there, it looks for it in the object's prototype. If the property is found on the prototype, the lookup succeeds, and JavaScript pretends that it found the property on the original object. Every function has an explicit property, known as prototype. When a function is used in a constructor call, JavaScript makes the value of this property the prototype of the newly created object:

let shape = Shape(2, 3);
Object.getPrototypeOf(shape) == Shape.prototype; // => true

All instances of a class have the same prototype. This makes the prototype the perfect place to define properties that are shared between instances of the class. To illustrate this, let's add a member function to the class Shape:

Shape.prototype.draw = function () {
    throw Error("not yet implemented");
}
let shape = Shape(2, 3);
Shape.draw(); // => Error: not yet implemented

Inheritance and Constructors

Suppose we want to create a new class, Circle, and inherit it from Shape. Since every Circle is also a Shape, the constructor for Shape must be called every time we call the constructor for Circle. Since JavaScript does not have native support for inheritance, it doesn't do this automatically. Instead, we need to call the constructor for Shape explicitly. The resulting constructor looks as follows:

function Circle(x, y, radius) {
   if (!this)
       return new Circle(x, y, radius);
   Shape.call(this, x, y);
   this.radius = radius;
}

Note that the constructor for Shape is called as an ordinary function, and reuses the object created for the constructor call to Circle. Had we used a constructor call instead, the constructor for Shape would have been applied to a different object than the constructor for Circle. We can now use the above constructor to create instances of the class Circle:

let circle = Circle(2, 3, 5);
circle instanceof Circle; // => true
circle.x; // => 2
circle.y; // => 3
circle.radius; // => 5

Inheritance and Prototypes

There is a problem with the definition of Circle in the previous section that we have glossed over thus far. Consider the following:

let circle = Circle(2, 3, 5);
circle.draw(); // => TypeError: circle.draw is not a function

This is not quite right. The method draw is defined on instances of Shape, so we definitely want it to be defined on instances of Circle. The problem is that draw is defined on the prototype of Shape, but not on the prototype of Circle. We could of course copy every property from the prototype of Shape over to the prototype of Circle, but this is needlessly inefficient. Instead, we use a clever trick, based on the observation that prototypes are ordinary objects. Since prototypes are objects, they have a prototype as well. We can thus override the prototype of Circle with an object which prototype is the prototype of Shape.

Circle.prototype = Object.create(Shape.prototype);

Now when JavaScript looks for the method draw on an instance of Circle, it first looks for it on the object itself. When it cannot find the property there, it looks for it on the prototype of Circle. When it cannot find the property there either, it looks for it on Shape, at which point the lookup succeeds. The resulting behavior is what we were aiming for.

Inheritance and Instanceof

The single line of code we added in the previous section solved the problem with prototypes, but introduced a new problem with the instanceof operator. Consider the following:

let circle = Circle(2, 3, 5);
circle instanceof Shape; // => false

Since instances of Circle inherit from Shape, we definitely want the result of this expression to be true. To understand why it is not, we need to understand how instanceof works. Every prototype has a constructor property, which is a reference to the constructor for objects with this prototype. In other words:

Circle.prototype.constructor == Circle // => true

The instanceof operator compares the constructor property of the prototype of the left hand side with that of the right hand side, and returns true if they are equal. Otherwise, it repeats the comparison for the prototype of the right hand side, and so on, until either it returns true, or the prototype becomes null, in which case it returns false. The problem is that when we overrode the prototype of Circle with an object whose prototype is the prototype of Shape, we didn't correctly set its constructor property. This property is set automatically for the prototype property of a constructor, but not for objects created with Object.create. The constructor property is supposed to be non-configurable, non-enumberable, and non-writable, so the correct way to define it is as follows:

Circle.prototype = Object.create(Shape.prototype, {
    constructor: {
        value: Circle
    }
});

Overriding Methods

As a final example, we show how to override the stub implementation of the method draw in Shape with a more specialized one in Circle. Recall that JavaScript returns the first property it finds when walking the prototype chain of an object from the bottom up. Consequently, overriding a method is as simple as providing a new definition on the prototype of the subclass:

Circle.prototype.draw = function (ctx) {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius,
            0, 2 * Math.PI, false);
    ctx.fill();
};

With this definition in place, we get:

let shape = Shape(2, 3);
shape.draw(); // Error: not yet implemented 
let circle = Circle(2, 3, 5);
circle.draw(); // TypeError: ctx is not defined

which is the behavior we were aiming for.

Classes in the Add-on SDK

We have shown how to emulate classical inheritance in JavaScript using constructors and prototypes. However, as we have seen, this takes a significant amount of boilerplate code. The Add-on SDK team consists of highly trained professionals, but they are also lazy: that is why the SDK contains a helper function that handles this boilerplate code for us. It is defined in the module “core/heritage”:

const { Class } = require('sdk/core/heritage');

The function Class is a meta-constructor: it creates constructors that behave properly with respect to inheritance. It takes a single argument, which is an object which properties will be defined on the prototype of the resulting constructor. The semantics of Class are based on what we've learned earlier. For instance, to define a constructor for a class Shape in terms of Class, we can write:

let Shape = Class({
    initialize: function (x, y) {
        this.x = x;
        this.y = y;
    },
    draw: function () {
        throw new Error("not yet implemented");
    }
});

The property initialize is special. When it is present, the call to the constructor is forwarded to it, as are any arguments passed to it (including the this object). In effect, initialize specifies the body of the constructor. Note that the constructors created with Class automatically check whether they are called as constructors, so an explicit check is no longer necessary.

Another special property is extends. It specifies the base class from which this class inherits, if any. Class uses this information to automatically set up the prototype chain of the constructor. If the extends property is omitted, Class itself is used as the base class:

var shape = new Shape(2, 3);
shape instanceof Shape; // => true
shape instanceof Class; // => true

To illustrate the use of the extends property, let's redefine the constructor for the class Circle in terms of Class:

var Circle = Class({
    extends: Shape,
    initialize: function(x, y, radius) {
        Shape.prototype.initialize.call(this, x, y);
        this.radius = radius;
    },
    draw: function (context) {
        context.beginPath();
        context.arc(this.x, this.y, this.radius,
                    0, 2 * Math.PI, false);
        context.fill();
    }
});

Unlike the definition of Circle in the previous section, we no longer have to override its prototype, or set its constructor property. This is all handled automatically. On the other hand, the call to the constructor for Shape still has to be made explicitly. This is done by forwarding to the initialize method of the prototype of the base class. Note that this is always safe, even if there is no initialize method defined on the base class: in that case the call is forwarded to a stub implementation defined on Class itself.

The last special property we will look into is implements. It specifies a list of objects, which properties are to be copied to the prototype of the constructor. Note that only properties defined on the object itself are copied: properties defined on one of its prototypes are not. This allows objects to inherit from more than one class. It is not true multiple inheritance, however: no constructors are called for objects inherited via implements, and instanceof only works correctly for classes inherited via extends.

Document Tags and Contributors

Tags: 
  • Add-on SDK
 Contributors to this page: wbamberg, jpmedley, maybe, evold
 Last updated by: wbamberg, Nov 30, 2016, 2:06:33 PM
See also
  1. WebExtensions
  2. Getting started
    1. What are WebExtensions?
    2. Your first WebExtension
    3. Your second WebExtension
    4. Anatomy of a WebExtension
    5. Example WebExtensions
  3. How to
    1. Intercept HTTP requests
    2. Modify a web page
    3. Add a button to the toolbar
    4. Implement a settings page
  4. Concepts
    1. Using the JavaScript APIs
    2. User interface components
    3. Content scripts
    4. Match patterns
    5. Internationalization
    6. Content Security Policy
    7. Native messaging
  5. Porting
    1. Porting a Google Chrome extension
    2. Porting a legacy Firefox add-on
    3. Embedded WebExtensions
    4. Comparison with the Add-on SDK
    5. Comparison with XUL/XPCOM extensions
    6. Chrome incompatibilities
  6. Firefox workflow
    1. Temporary Installation in Firefox
    2. Debugging
    3. Getting started with web-ext
    4. web-ext command reference
    5. WebExtensions and the Add-on ID
    6. Publishing your WebExtension
  7. JavaScript APIs
    1. Browser support for JavaScript APIs
    2. alarms
    3. bookmarks
    4. browserAction
    5. browsingData
    6. commands
    7. contextMenus
    8. contextualIdentities
    9. cookies
    10. downloads
    11. events
    12. extension
    13. extensionTypes
    14. history
    15. i18n
    16. identity
    17. idle
    18. management
    19. notifications
    20. omnibox
    21. pageAction
    22. runtime
    23. sessions
    24. sidebarAction
    25. storage
    26. tabs
    27. topSites
    28. webNavigation
    29. webRequest
    30. windows
  8. Manifest keys
    1. applications
    2. author
    3. background
    4. browser_action
    5. chrome_url_overrides
    6. commands
    7. content_scripts
    8. content_security_policy
    9. default_locale
    10. description
    11. developer
    12. homepage_url
    13. icons
    14. manifest_version
    15. name
    16. omnibox
    17. options_ui
    18. page_action
    19. permissions
    20. short_name
    21. sidebar_action
    22. version
    23. web_accessible_resources
  9. Add-on SDK
  10. Getting started
    1. Installation
    2. Getting started
    3. Troubleshooting
  11. High-Level APIs
    1. addon-page
    2. base64
    3. clipboard
    4. context-menu
    5. hotkeys
    6. indexed-db
    7. l10n
    8. notifications
    9. page-mod
    10. page-worker
    11. panel
    12. passwords
    13. private-browsing
    14. querystring
    15. request
    16. selection
    17. self
    18. simple-prefs
    19. simple-storage
    20. system
    21. tabs
    22. timers
    23. ui
    24. url
    25. webextension
    26. widget
    27. windows
  12. Low-Level APIs
    1. /loader
    2. chrome
    3. console/plain-text
    4. console/traceback
    5. content/content
    6. content/loader
    7. content/mod
    8. content/symbiont
    9. content/worker
    10. core/heritage
    11. core/namespace
    12. core/promise
    13. dev/panel
    14. event/core
    15. event/target
    16. frame/hidden-frame
    17. frame/utils
    18. fs/path
    19. io/byte-streams
    20. io/file
    21. io/text-streams
    22. lang/functional
    23. lang/type
    24. loader/cuddlefish
    25. loader/sandbox
    26. net/url
    27. net/xhr
    28. places/bookmarks
    29. places/favicon
    30. places/history
    31. platform/xpcom
    32. preferences/event-target
    33. preferences/service
    34. remote/child
    35. remote/parent
    36. stylesheet/style
    37. stylesheet/utils
    38. system/child_process
    39. system/environment
    40. system/events
    41. system/runtime
    42. system/unload
    43. system/xul-app
    44. tabs/utils
    45. test/assert
    46. test/harness
    47. test/httpd
    48. test/runner
    49. test/utils
    50. ui/button/action
    51. ui/button/toggle
    52. ui/frame
    53. ui/id
    54. ui/sidebar
    55. ui/toolbar
    56. util/array
    57. util/collection
    58. util/deprecate
    59. util/list
    60. util/match-pattern
    61. util/object
    62. util/uuid
    63. window/utils
  13. Firefox for Android
  14. Getting started
    1. Walkthrough
    2. Debugging
    3. Code snippets
  15. APIs
    1. Accounts.jsm
    2. BrowserApp
    3. HelperApps.jsm
    4. Home.jsm
    5. HomeProvider.jsm
    6. JavaAddonManager.jsm
    7. NativeWindow
    8. Notifications.jsm
    9. PageActions.jsm
    10. Prompt.jsm
    11. RuntimePermissions.jsm
    12. Snackbars.jsm
    13. Sound.jsm
    14. Tab
  16. Legacy
  17. Restartless extensions
    1. Overview
  18. Overlay extensions
    1. Overview
  19. Themes
  20. Lightweight themes
    1. Overview
  21. Complete themes
    1. Overview
  22. Publishing add-ons
  23. Guides
    1. Signing and distribution overview
    2. Submit an add-on
    3. Review policies
    4. Developer agreement
    5. Featured add-ons
    6. Contact addons.mozilla.org
  24. Community and support
  25. Channels
    1. Add-ons blog
    2. Add-on forums
    3. Stack Overflow
    4. Development newsgroup
    5. IRC Channel