In JavaScript there are 2 options for class inheritance: functional(regular - well known by other OO languages) and prototype(own style based on "cloning").
As you can see, mocha and chai libraries will be downloaded by SRIPT tag. Also we set up mocha for using BDD, exported "assert" for simple usage and executed mocha test running.
In addition, we need just 2 files:
- we will be creating them step-by-step during implementation. First - function for MyScript, next - testblock for that function in MyScriptTest.js.
- it has 2 private variables: _x and _y, we also created just "getters"(getX and getY) for this variables - so our class in "immutable": we can not change it after creation. Beside the we added "toString" method for printing our object in "user friendly" style.
Let's test it:
Now let's imagine, we have to extend our class for adding more(we want to add information about color to our point) functionality:
Everything looks more or less similar to what we have in other OO languages:
- we can to call "parent" constructor : FuncPoint.call(this, x, y);
so, no needs to create own variables _x and _y - the will be "inherited" from parent(FuncPoint) class.
- we can add additional properties(color) and methods(getColor)
- we can "override" methods by just defining the method with the same name: self.toString = function () {....} and if we want to use "parent" function in "overriding" we can "copy" it to another variable(var parentToString = self.toString;) and use from it: return parentToString() + "#" + _color;
Test:
Everything is working, functional inheritance is clear and simple for understanding.
Concept of "class" is absent in JavaScript, there are just "objects" which are created by functions. So when we are creating object by new.... operator we are creating object by "prototype".
For example, we have regular function which creates object:
When we creating object "p = new ProtoPoint" - we are creating object with property "__proto__" which is our case was inherited from Object.prototype property:
there are a lot of interesting things inside it:
For example we have a methods which were inherited from Object class like "hasOwnProperty" "toString" and others.
- it's just another option of defining object methods.
Test:
Everything is working just like methods were defined inside function "ProtoPoint". But where is one not very good thing here: our variables x and y are visible outside and we can't make them private:
For prototype inheritance we can just create independent standalone class, and ones we decided to make it "inherited" from another - we can just change it __proto__ object:
For calling "parent" functions on overriding we can use ProtoPoint.prototype.PARENT_FUNCTION.call
Let's test it:
1. Preparation
First of all we need several files:- html file for displaying test results: test.html- our script file with algorythm implementation: MyScript.js- file with test scripts for previous step: MyScriptTest.js- libraries mocha.js and chai.js - they will be used as external scripts.All this files I put to one directory. Here is the content of test result file: test.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <!-- Mocha css --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mocha/2.1.0/mocha.css"> <!-- Mocha dependency --> <script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/2.1.0/mocha.js"></script>
<!-- Mocha: setup BDD --> <script> mocha.setup('bdd'); </script>
<!-- chai dependency --> <script src="https://cdnjs.cloudflare.com/ajax/libs/chai/2.0.0/chai.js"></script> <!-- export assert --> <script> var assert = chai.assert; </script> </head> <body> <!-- script which should be tested --> <script src="MyScript.js"></script> <!-- test itself --> <script src="MyScriptTest.js"></script>
<!-- element with id="mocha" for test results --> <div id="mocha"></div> <!-- run tests! --> <script> mocha.run(); </script> </body></html>
As you can see, mocha and chai libraries will be downloaded by SRIPT tag. Also we set up mocha for using BDD, exported "assert" for simple usage and executed mocha test running.
In addition, we need just 2 files:
<!-- script which should be tested --><script src="MyScript.js"></script> <!-- test itself --><script src="MyScriptTest.js"></script>
- we will be creating them step-by-step during implementation. First - function for MyScript, next - testblock for that function in MyScriptTest.js.
2. Functional inheritance
Let's create a regular class:function FuncPoint(x, y) { var _x = x, _y = y, self = this; self.getX = function () { return _x; }; self.getY = function () { return _y; }; self.toString = function () { return "[" + _x + "," + _y + "]"; }; return self; }
- it has 2 private variables: _x and _y, we also created just "getters"(getX and getY) for this variables - so our class in "immutable": we can not change it after creation. Beside the we added "toString" method for printing our object in "user friendly" style.
Let's test it:
it("should be getX, getY, toString functions in FuncPoint class", function () { var p = new FuncPoint(1, 2); assert.equal(p.getX(), 1); assert.equal(p.getY(), 2); assert.equal(p.toString(), "[1,2]"); });
2.1 Child class in functional inheritance style.
Now let's imagine, we have to extend our class for adding more(we want to add information about color to our point) functionality:function FuncColoredPoint(x, y, color) { FuncPoint.call(this, x, y); var _color = color, self = this; self.getColor = function () { return _color; }; var parentToString = self.toString; self.toString = function () { return parentToString() + "#" + _color; }; return self; }
Everything looks more or less similar to what we have in other OO languages:
- we can to call "parent" constructor : FuncPoint.call(this, x, y);
so, no needs to create own variables _x and _y - the will be "inherited" from parent(FuncPoint) class.
- we can add additional properties(color) and methods(getColor)
- we can "override" methods by just defining the method with the same name: self.toString = function () {....} and if we want to use "parent" function in "overriding" we can "copy" it to another variable(var parentToString = self.toString;) and use from it: return parentToString() + "#" + _color;
Test:
it("should inherited getX and getY functions beside own function getColor and overriden function toString in FuncColoredPoint class", function () { var p = new FuncColoredPoint(2, 3, "RED"); assert.equal(p.getX(), 2); assert.equal(p.getY(), 3); assert.equal(p.getColor(), "RED"); assert.equal(p.toString(), "[2,3]#RED"); });
Everything is working, functional inheritance is clear and simple for understanding.
3. Prototype inheritance
Every JavaScript object has a prototype. The prototype is also an object.
All JavaScript objects inherit their properties and methods from their prototype.
All JavaScript objects inherit the properties and methods from their prototype.
Objects created using an object literal, or with new Object(), inherit from a prototype called Object.prototype.
Concept of "class" is absent in JavaScript, there are just "objects" which are created by functions. So when we are creating object by new.... operator we are creating object by "prototype".
For example, we have regular function which creates object:
function ProtoPoint(x, y) { this.x = x; this.y = y; }
When we creating object "p = new ProtoPoint" - we are creating object with property "__proto__" which is our case was inherited from Object.prototype property:
there are a lot of interesting things inside it:
For example we have a methods which were inherited from Object class like "hasOwnProperty" "toString" and others.
3.1 Adding methods to prototype.
"__proto__" is just a property, so we can add to it methods:ProtoPoint.prototype.getX = function () { return this.x; }; ProtoPoint.prototype.getY = function () { return this.y; }; ProtoPoint.prototype.toString = function () { return "[" + this.x + "," + this.y + "]"; };
- it's just another option of defining object methods.
Test:
it("should getX, getY, toString functions added to PROTOTYPE", function () { var p = new ProtoPoint(4, 5); assert.equal(p.getX(), 4); assert.equal(p.getY(), 5); assert.equal(p.toString(), "[4,5]"); });
Everything is working just like methods were defined inside function "ProtoPoint". But where is one not very good thing here: our variables x and y are visible outside and we can't make them private:
it("should be visible outside local variable - we can't make them private :(", function () { var p = new ProtoPoint(6, 7); assert.equal(p.x, 6); assert.equal(p.y, 7); });
3.2 Prototype inheritance.
For prototype inheritance we can just create independent standalone class, and ones we decided to make it "inherited" from another - we can just change it __proto__ object:function ProtoColoredPoint(x, y, color) { ProtoPoint.call(this, x, y); this.color = color; } ProtoColoredPoint.prototype = Object.create(ProtoPoint.prototype); ProtoColoredPoint.prototype.getColor = function () { return this.color; }; ProtoColoredPoint.prototype.toString = function () { return ProtoPoint.prototype.toString.call(this) + "#" + this.color; };
For calling "parent" functions on overriding we can use ProtoPoint.prototype.PARENT_FUNCTION.call
Let's test it:
it("should be class ProtoColoredPoint with inherited methods form ProtoPoint", function () { var p = new ProtoColoredPoint(8, 9, "BLACK"); assert.equal(p.getX(), 8); assert.equal(p.getY(), 9); assert.equal(p.getColor(), "BLACK"); assert.equal(p.toString(), "[8,9]#BLACK"); });