simple inheritance in javascript, with private, internal and public methods and members

here is a spike, to show a simple way to implement pseudo-classical code reuse in javascript, using inheritance.

javascript is a prototyped language, so the normal classical inheritance is not exactly possible.

note: some say that code re-use in javascript is better done using Decorator pattern, or using composition.
however, most programmers are familiar with classical inheritance and so this could be a convenient way to structure code for reuse, in a team of developers.

reference book:
http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742/ref=sr_1_1?s=books&ie=UTF8&qid=1377268395&sr=1-1&keywords=javascript+the+good+parts

1:  //////////////////////////////////////////////////////////////////////////////  
2:  //inheritance, with following features:  
3:  //- overriding of base methods  
4:  //- private members  
5:  //- shared internal members (bit like protected, only works UP as well as down the hierarchy).  
6:  //- simple coding of derived class  
7:  //  
8:  // ref: JavaScript the Good Parts - D. Crockford, pages 52-55.  
9:  //////////////////////////////////////////////////////////////////////////////  
10:  //helper functions  
11:  Function.prototype.addMethod = function (name, func) {  
12:    this.prototype[name] = func;  
13:    return this;  
14:  };  
15:  Object.addMethod('getMethodInBase', function (name) {  
16:    var that = this;  
17:    var method = that[name];  
18:    return function () {  
19:      return method.apply(that, arguments);  
20:    };  
21:  });  
22:  //////////////////////////////////////////////////////////////////////////////  
23:  //tryout:  
24:  var inheritanceTryout2 = {};  
25:  ////////////////////////////////////////////  
26:  //class CarBase  
27:  //internalMembers: this is a bit like a protected member, but is shared UP as well as down the hierarchy.  
28:  //when client creates an instance, they need to pass in new empty object like this {}  
29:  //We need to do this, because we need an internal object shared, and we cannot assume that this class is the most derived class.  
30:  //  
31:  inheritanceTryout2.CarBase = function (color, name, internalMembers) {  
32:    if (!internalMembers)  
33:    {  
34:      internalMembers = {};  
35:    }  
36:    //secret is private to the base object  
37:    var secret = {  
38:      name: name  
39:    };  
40:    //internal member:  
41:    internalMembers.color = color;  
42:    var that = {};  
43:    //add a public 'priviledged' method, that has access to the private members:  
44:    that.getName = function () {  
45:      return 'CarBase.getName() - ' + secret.name;  
46:    };  
47:    //another method:  
48:    that.getNameLength = function () {  
49:      return 'CarBase.getNameLength() - ' + secret.name.length;  
50:    };  
51:    //add an internal method. this method can only be called by base or derived part of the object. (NOT public).  
52:    internalMembers.getColor = function () {  
53:      return 'CarBase.getColor() - internal color:' + internalMembers.color;  
54:    };  
55:    return that;  
56:  };  
57:  ////////////////////////////////////////////  
58:  //class Porche  
59:  inheritanceTryout2.Porche = function (color, name, style, internalMembers) {  
60:    if (!internalMembers) {  
61:      internalMembers = {};  
62:    }  
63:    //secret is private to this part of the object (NOT accessible by base or derived).  
64:    var secret = {  
65:      style: style  
66:    };  
67:    //inherit from CarBase:  
68:    var that = inheritanceTryout2.CarBase(color, name, internalMembers);  
69:    //get access to a method in base class:  
70:    var baseGetName = that.getMethodInBase('getName');  
71:    //override a base public method:  
72:    that.getName = function () {  
73:      return 'Porche.getName() - porche name: ' + baseGetName();  
74:    };  
75:    //add a public method to this class:  
76:    that.getPorcheStyle = function () {  
77:      return 'Porche.getPorcheStyle() - ' + secret.style;  
78:    };  
79:    //add a public method, that calls an internal method:  
80:    that.getColorPublic = function () {  
81:      return 'Porche.getColorPublic() - colour from internal data: ' + internalMembers.getColor();  
82:    };  
83:    return that;  
84:  };  
85:  ////////////////////////////////////////////  
86:  //class PorcheConvertible  
87:  inheritanceTryout2.PorcheConvertible = function (color, name, style, roofState, internalMembers) {  
88:    if (!internalMembers) {  
89:      internalMembers = {};  
90:    }  
91:    //secret is private to this part of the object (NOT accessible by base or derived).  
92:    var secret = {  
93:      roofState: roofState  
94:    };  
95:    //inherit from Porche:  
96:    var that = inheritanceTryout2.Porche(color, name, style, internalMembers);  
97:    //get access to a method in base class:  
98:    var baseGetName = that.getMethodInBase('getName');  
99:    //override a base public method:  
100:    that.getName = function () {  
101:      return 'PorcheConvertible.getName() - porche name: ' + baseGetName();  
102:    };  
103:    //add a public method to this class:  
104:    that.getRoofState = function () {  
105:      return 'PorcheConvertible.getRoofState() - ' + secret.roofState;  
106:    };  
107:    that.setRoofState = function (value) {  
108:      secret.roofState = value;  
109:    };  
110:    return that;  
111:  };  
112:  ////////////////////////////////////////////  
113:  //class Ford  
114:  inheritanceTryout2.Ford = function (color, name, internalMembers) {  
115:    if (!internalMembers) {  
116:      internalMembers = {};  
117:    }  
118:    //inherit from CarBase:  
119:    var that = inheritanceTryout2.CarBase(color, name, internalMembers);  
120:    //get access to a method in the base class:  
121:    var baseGetName = that.getMethodInBase('getName');  
122:    //override a base public method:  
123:    that.getName = function (n) {  
124:      return 'Ford.getName(): ' + baseGetName();  
125:    };  
126:    return that;  
127:  };  
128:  inheritanceTryout2.write = function (msg) {  
129:    jsUtils.write(msg, 'carMessages');  
130:  };  
131:  //////////////////////////////////////////////////////////////////////////////  
132:  //TEST  
133:  $(function () {  
134:    inheritanceTryout2.test();  
135:  });  
136:  inheritanceTryout2.test = function () {  
137:    //create a Porche, passing in an internal object for internal data shared by base + derived parts:  
138:    var porche1 = inheritanceTryout2.Porche('pink', 'Cammy 1', 'classic');  
139:    //create another Porche:  
140:    var porche2 = inheritanceTryout2.Porche('violet', 'Cammy 2', 'groovy');  
141:    //create a Ford:  
142:    var ford1 = inheritanceTryout2.Ford('blue', 'Ford. 1');  
143:    //create a PorcheConvertible:  
144:    var porcheConv1 = inheritanceTryout2.PorcheConvertible('black', 'Porche 1', 'modern', 'up');  
145:    inheritanceTryout2.write('car testing ... ');  
146:    inheritanceTryout2.write('porche1: ' + porche1.getName());  
147:    inheritanceTryout2.write('porche1 style: ' + porche1.getPorcheStyle());  
148:    inheritanceTryout2.write('porche2: ' + porche2.getName());  
149:    inheritanceTryout2.write('porche2 style: ' + porche2.getPorcheStyle());  
150:    inheritanceTryout2.write('porche2 color: ' + porche2.getColorPublic());  
151:    inheritanceTryout2.write('ford1: ' + ford1.getName());  
152:    inheritanceTryout2.write('ford1: calling base public method: ' + ford1.getNameLength());  
153:    inheritanceTryout2.write('porcheConvertible1: ' + porcheConv1.getName());  
154:    inheritanceTryout2.write('porcheConvertible1 style: ' + porcheConv1.getPorcheStyle());  
155:    inheritanceTryout2.write('porcheConvertible1 color: ' + porcheConv1.getColorPublic());  
156:    inheritanceTryout2.write('porcheConvertible1: roof state: ' + porcheConv1.getRoofState());  
157:    porcheConv1.setRoofState('down');  
158:    inheritanceTryout2.write('porcheConvertible1: roof state: ' + porcheConv1.getRoofState());  
159:    //add global reference, so can access from browser console:  
160:    inheritanceTryout2.porche1 = porche1;  
161:    inheritanceTryout2.porche2 = porche2;  
162:    inheritanceTryout2.ford1 = ford1;  
163:    inheritanceTryout2.porcheConv1 = porcheConv1;  
164:  };  

Comments