ref –
http://julien.richard-foy.fr/blog/2011/10/30/functional-inheritance-vs-prototypal-inheritance/
https://medium.com/javascript-scene/3-different-kinds-of-prototypal-inheritance-es6-edition-32d777fa16c9
http://therealmofcode.com/posts/2012/11/functional-inheritance-javascript.html
https://stackoverflow.com/questions/2800964/benefits-of-prototypal-inheritance-over-classical
Functional Inheritance
The properties you attach to the returning object are class public interface. Functions and properties you define inside function, but not as a part of the returning object, are private members accessible because of closure and invisible because they were not returned.
1) In the parent function, we first create an object on the local stack.
2) We then create private properties.
3) Create public functions and properties. Can access the private properties previously created by scope.
4) Do custom construction with your properties
5) return the stack object
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
function TestClass(/*arguments*/) { // create stack object to be returned var obj = {}; var privateField; // create private variables like so function privateFunction() {} // create private functions like so // attach public properties to the stack object obj.publicField = ""; obj.publicFunction = function() {}; // construction logic here return obj; // return it } |
In the child class, we inherit from the parent by:
1) Creating an instance of the parent.
over-ride parent function by:
2) over-ride parent function by having a reference to it, super_functionName
3) Then adding a property function of the same name to obj
4) In that function, call the reference super_functionName
5) add custom implementation after that
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
function TestClassSpecific(/*arguments*/) { // initial object is instance of our test class var obj = TestClass(); // want to over-ride parent class // 1) save reference to parent class function var super_publicFunction = obj.publicFunction; // 2) over-riding parent class function like so: obj.publicFunction = function() { // call parent class function super_publicFunction(); // do your over-ride code here }; // construction logic here return obj; } |
Now that you have created the parent and child, when using them, simply call the child function
and have it return you an object.
Execute the public properties by calling their names on that object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
function TestClass(/*arguments*/) { console.log("create TestClass object"); var obj = {}; var privateField; // create private variables like so function privateFunction() {} // create private functions like so // attach public properties to the stack object obj.publicField = "Ha Doooo ken!"; obj.publicFunction = function() { console.log("TestClass: Super!! :D") console.log("TestClass: Manipulating private fields here by scope"); }; // construction logic here console.log("returning TestClass object"); return obj; // return it } function TestClassSpecific(/*arguments*/) { // initial object is instance of our test class var obj = TestClass(); // want to over-ride parent class // 1) save reference to parent class function var super_publicFunction = obj.publicFunction; // 2) over-riding parent class function like so: obj.publicFunction = function() { // call parent class function super_publicFunction(); // do your over-ride code here console.log("TestClassSpecific: over-riding the public function...") }; // construction logic here return obj; } var obj1 = TestClassSpecific() // create instance // execute functionality obj1.publicFunction(); console.log(obj1.publicField) |
Protected properties between parent and child
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
function Parent(my) { console.log("create TestClass object"); var obj = {}; // private properties var privateField; function privateFunction() {} // protected properties. Only children can access this my = my || {}; my.protectedField = "pro teck ted field"; my.protectedFunction = function() { console.log("TestClass: protectedFunction") console.log("TestClass: Your protected field is: " + my.protectedField); } // public properties obj.publicField = "Ha Doooo ken!"; obj.publicFunction = function() { console.log("TestClass: Super!! :D") console.log("TestClass: Manipulating private fields here by scope"); }; // construction logic here console.log("returning TestClass object"); return obj; // return it } function Child(/*arguments*/) { var my = {}; // code outside can't access this object // but we (being a child) can access parent's protected properties // initial object is instance of our test class var obj = Parent(my); // want to over-ride parent class // 1) save reference to parent class function var super_publicFunction = obj.publicFunction; // 2) over-riding parent class function like so: obj.publicFunction = function() { // call parent class function super_publicFunction(); // do your over-ride code here console.log("TestClassSpecific: over-riding the public function...") }; obj.accessProtected = function() { console.log("TestClassSpecific - accessProtected") my.protectedFunction() } // construction logic here return obj; } var obj1 = Child() obj1.publicFunction(); console.log(obj1.publicField) obj1.accessProtected(); |
Pros
– SIMPLICITY: This pattern is very straightforward and easy for beginners to learn.
– Offers truly private and protected members.
– Protects against common javascript pitfalls, specifically if you forget to specify “new” keyword during class creation or if you are using “this” keyword inside functions.
Cons
– Requires more memory than Pseudoclassical inheritance pattern. With every new instance of a class memory is reserved for all functions and fields inside that class.
– Types cannot be tested using “instanceof” keyword.
– Javascript minimizers might not perform as good as with Pseudoclassical pattern as there is no way for minimizer to safely rename members of parent class.
Benefits of Prototypal over Functional Inheritance
Prototypal inheritance is all about objects. Objects inherit properties from other objects. That’s all there is to it. There are two ways of creating objects using prototypal inheritance:
Create a brand new object
for example, create object, add properties.
1 2 3 |
var circle = { radius: 5 }; |
We can calculate the area and the circumference of the circle from its radius:
1 2 3 4 5 6 7 8 |
circle.area = function () { var radius = this.radius; return Math.PI * radius * radius; }; circle.circumference = function () { return 2 * Math.PI * this.radius; }; |
Now I want to create another circle of radius 10. We create a brand new object by using functionality from our circle object.
1 2 3 4 5 |
var circle2 = { radius: 10, area: circle.area, circumference: circle.circumference }; |
Clone an existing object and extend it
JavaScript offers two ways to clone an object:
1) delegation
– Any changes to the prototype are automatically reflected on all its clones.
– Property access is slower because it may need to traverse up the prototype chain.
– Objects may only delegate to a single prototype in JavaScript.
How do we do clone an object via delegation?
Where a is an empty object, and its __proto__ is “someObj”, which will act as the prototype literal object of a.
ref – http://chineseruleof8.com/code/index.php/2018/04/13/object-create/
1 |
var a = Object.create(someObj) |
2) concatenation
– Any changes to the prototype need to be propagated to all its clones.
– Property access is faster because inherited properties are copied.
– Objects may copy properties from any number of prototypes.
How do we do clone an object via concatenation?
ECMAscript 6 introduced Object.assign() to achieve this natively in Javascript.
The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.
example here:
But how is this simple?
We have to call Object.create or Object.assign, and do all these assignment ourselves. Its bother some. The key is to implement creation functions for you like so:
1 2 3 4 5 6 7 |
function createCircle(radius) { var newCircle = Object.create(circle); newCircle.radius = radius; return newCircle; } var circle2 = createCircle(10); |
… even better …
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
var circle = { radius: 5, // implement a create function here create: function (radius) { var circle = Object.create(this); circle.radius = radius; return circle; }, area: function () { var radius = this.radius; return Math.PI * radius * radius; }, circumference: function () { return 2 * Math.PI * this.radius; } }; var circle2 = circle.create(10); console.log(circle2) console.log(circle2.__proto__) console.log(circle2.radius) |
— circle —
{ radius: 10 }
{ radius: 5,
create: [Function: create],
area: [Function: area],
circumference: [Function: circumference] }
10