Nevertheless, object-orientation remains the dominant paradigm for large applications and their underlying frameworks: its focus on code reuse via inheritance well serves the latter, and its data modeling approach is natural for software like simulations, UX toolkits, and enterprise logic.
Considering the fundamental things any widget needs, we have a location, a bounding rectangle, a callback function to receive click events, and a render() method for the widget to draw itself. So we define the basic geometry, and a base Widget class:
Our basic Widget requires a Canvas’ graphics context to draw itself onto and a bounding rectangle to locate it on the screen. We also supply a callback for clicking it; and a render() method, with a default fallback used if no renderer was given. Here’s how we use these modules in the browser:
Everything looks just as we expect from typical OO: we create object instances by writing the ‘new’ keyword before invoking a constructor. In the markup, the only unusual thing to note is the type attribute, ‘module’, on the inline <script> tag: this is required for inline scripts to use the ‘import’ feature.
So what other aspects of object orientation can we illustrate with this little widget model? Some inheritance would be nice. For differently shaped widgets, we’ll want different render() methods, and different ways to detect clicks. Prior to ES6, inheritance was done by manipulating the ‘prototype’ member that all objects inherently possess.
This is still how it works under the hood, but now we can use the convenient ‘extends’ and ‘super’ keywords:
Above, we’ve altered main() to instantiate a basic Widget, and one of its subclass, CircleButton. Let’s see the result of overriding the base implementation of render().