{"id":67,"date":"2014-02-07T15:53:46","date_gmt":"2014-02-07T23:53:46","guid":{"rendered":"http:\/\/www.kolls.net\/blog\/?p=67"},"modified":"2014-02-07T15:53:46","modified_gmt":"2014-02-07T23:53:46","slug":"class-like-inheritance-overriding-and-superclass-calls-in-javascript","status":"publish","type":"post","link":"https:\/\/www.kolls.net\/blog\/?p=67","title":{"rendered":"&#8220;Class&#8221;-like Inheritance, Overriding, and Superclass Calls in JavaScript"},"content":{"rendered":"<p>JavaScript is a prototype-based language with objects, rather than classes, as its fundamental construct.  However, the use of functions to approximate classes (complete with the &#8220;new&#8221; keyword) is very common.  Although we&#8217;re often told that JavaScript supports inheritance, it&#8217;s not entirely obvious how the class-like inheritance works in a prototype-based language such as JavaScript.  Adding to the confusion is various syntax approaches that may be used to devise objects and &#8220;classes&#8221;.<\/p>\n<p>As an aside, the techniques shown in this post require ES5 support, which, for example, excludes IE 8 and below.  There are fairly simple shims that can be used to enable support in older browsers.<\/p>\n<p>We&#8217;ll first consider a &#8220;naive&#8221; approach to defining a class in JavaScript, then discuss why this approach isn&#8217;t suitable for an inheritance scenario.<\/p>\n<pre class=\"prettyprint\">\r\nfunction BaseClass(initialValue) {\r\n  this.value = initialValue;\r\n  this.compute = function() { return this.value + 1; };\r\n};\r\n<\/pre>\n<p>This class seems to work fine:<\/p>\n<pre class=\"prettyprint\">\r\nvar instance = new BaseClass(3);\r\nconsole.log(instance.compute()); \/\/ outputs 4\r\n<\/pre>\n<p>However, this approach is not suitable for inheritance.  The function &#8220;compute&#8221; (and any other functions defined in this way) will be defined not on the &#8220;class&#8221; (prototype) but on the object itself.  When overridden, there will be no reasonable way to access the base function, because the override will actually replace the original function on that object.  Thus, functions should be defined on the prototype, while fields are defined on the object itself.  If you define a field on the prototype, it is like a <b>static member<\/b> in C#.<\/p>\n<pre class=\"prettyprint\">\r\nfunction BaseClass(initialValue) {\r\n  this.value = initialValue;\r\n};\r\nBaseClass.prototype.compute = function() { return this.value + 1; };\r\n<\/pre>\n<p>This produces the same result as before, but now the function is defined on the prototype instead of the object, so it can be referenced by derived classes.<\/p>\n<p>Now imagine we want to construct a derived class.  There are two important considerations: ensuring that the super-constructor is called correctly, and ensuring that &#8220;instanceof&#8221; continues to give the correct result.  There is no &#8220;base&#8221; or &#8220;super&#8221; keyword in JavaScript, so you must use the actual name of the base class whenever you want to refer to it.  <\/p>\n<pre class=\"prettyprint\">\r\nfunction DerivedClass(initialValue, secondValue) {\r\n  BaseClass.call(this, initialValue); \/\/ *** Call super-constructor\r\n  this.anotherValue = secondValue; \/\/ Additional field\r\n};\r\n\/\/ *** Establish prototype relationship\r\nDerivedClass.prototype = Object.create(BaseClass.prototype); \r\n\r\n\/\/ Additional function\r\nDerivedClass.prototype.anotherCompute = \r\n   function() { return this.compute() + this.anotherValue; }; \r\n<\/pre>\n<p>Now let&#8217;s look at the things we can do with an instance of the derived class.  They should all work as expected.<\/p>\n<pre class=\"prettyprint\">\r\nvar instance2 = new DerivedClass(4, 6);\r\n\/\/ base method with base fields: outputs 5 (4+1)\r\nconsole.log(instance2.compute()); \r\n\r\n\/\/ derived method with base method and derived field: \r\n\/\/ outputs 11 ((4+1)+6)\r\nconsole.log(instance2.anotherCompute()); \r\n\r\n\/\/ outputs true\r\nconsole.log(instance2 instanceof DerivedClass); \r\n\r\n\/\/ ALSO outputs true\r\nconsole.log(instance2 instanceof BaseClass); \r\n<\/pre>\n<p>Now imagine we want to create a derived class that overrides some behavior of the base class.  First, we will create a class inheriting from the previous derived class, and override the compute method. <\/p>\n<pre class=\"prettyprint\">\r\nfunction ThirdClass(initialValue, secondValue) {\r\n  \/\/ In this case, the \"base\" class \r\n  \/\/ of this class is called \"DerivedClass\"\r\n  DerivedClass.call(this, initialValue, secondValue); \r\n\r\n  \/\/ no new member fields\r\n};\r\nThirdClass.prototype = Object.create(DerivedClass.prototype); \r\n\r\n\/\/ override base function compute\r\nThirdClass.prototype.compute = function() { return 100; }; \r\n<\/pre>\n<p>Let&#8217;s see this overridden function in action.  The override also impacts, for example, the &#8220;anotherCompute&#8221; function since it calls compute.  The overriding works exactly as you would expect as in, say, C#.<\/p>\n<pre class=\"prettyprint\">\r\nvar instance3 = new ThirdClass(4, 6);\r\n\/\/ overridden method always outputs 100\r\nconsole.log(instance3.compute()); \r\n\r\n\/\/ derived method with OVERRIDDEN method (inside) and derived field: \r\n\/\/ outputs 106 (100+6)\r\nconsole.log(instance3.anotherCompute()); \r\n\r\n\/\/ confirm that the compute has only been overridden \r\n\/\/ for instances of ThirdClass: \r\n\/\/ outputs 5, as before\r\nconsole.log(instance2.compute()); \r\n<\/pre>\n<p>One final task remains: can we override a function, and then still make a call to the base class version of that function?  In this case, we&#8217;ll replace the definition of compute in the third class with one that overrides compute while also making a class to the base class definition of compute.  This is where the technique for function definition (defining on the prototype vs. defining on the object) becomes critical.  Since the functions are actually defined on the prototypes, we can call back to the base class prototype for that version of the function.  Remember that &#8220;ThirdClass&#8221; derives from &#8220;DerivedClass&#8221;<\/p>\n<pre class=\"prettyprint\">\r\n\/\/ override base function compute, but still use it in this function\r\nThirdClass.prototype.compute = function() { \r\n  return DerivedClass.prototype.compute.call(this) * 5; \r\n}; \r\n<\/pre>\n<p>Now we&#8217;ll retry the two instance3 calls from earlier:<\/p>\n<pre class=\"prettyprint\">\r\n\/\/ overridden method calls base implementation of compute, \r\n\/\/ which returns 5, and multiplies it by 5. output: 25\r\nconsole.log(instance3.compute()); \r\n\r\n\/\/ derived method with overridden method\r\n\/\/ (inside, which calls base implementation) \r\n\/\/ and derived field: outputs 31 ((5*5)+6)\r\nconsole.log(instance3.anotherCompute()); \r\n<\/pre>\n<p>We now have a reliable technique for constructing &#8220;class&#8221;es in JavaScript which fully support inheritance, overriding, and superclass calls in the way conventional from class-based object-oriented languages.<\/p>\n<p>Remember, though, that this is &#8220;writing C#\/Java in JavaScript&#8221; and should be used sparingly. Also, some authors believe <a href=\"http:\/\/sebastiansylvan.com\/2010\/12\/03\/implementation-inheritance-considered-harmful\/\">implementation inheritance is usually bad<\/a> even in languages which support it as a primary form of reuse.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>JavaScript is a prototype-based language with objects, rather than classes, as its fundamental construct. However, the use of functions to approximate classes (complete with the &#8220;new&#8221; keyword) is very common. Although we&#8217;re often told that JavaScript supports inheritance, it&#8217;s not &hellip; <a href=\"https:\/\/www.kolls.net\/blog\/?p=67\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[7],"tags":[],"_links":{"self":[{"href":"https:\/\/www.kolls.net\/blog\/index.php?rest_route=\/wp\/v2\/posts\/67"}],"collection":[{"href":"https:\/\/www.kolls.net\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kolls.net\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kolls.net\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kolls.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=67"}],"version-history":[{"count":9,"href":"https:\/\/www.kolls.net\/blog\/index.php?rest_route=\/wp\/v2\/posts\/67\/revisions"}],"predecessor-version":[{"id":76,"href":"https:\/\/www.kolls.net\/blog\/index.php?rest_route=\/wp\/v2\/posts\/67\/revisions\/76"}],"wp:attachment":[{"href":"https:\/\/www.kolls.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=67"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kolls.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=67"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kolls.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=67"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}