Combining inheritance with the module pattern - javascript

I like the module pattern that returns constructors as described in:
http://elegantcode.com/2011/02/15/basic-javascript-part-10-the-module-pattern/
However I am not sure how to inherit from an object that is implemented with this pattern. Suppose I have a parent object implemented thus...
namespace('MINE');
MINE.parent = (function() {
// private funcs and vars here
// Public API - constructor
var Parent = function (coords) {
// ...do constructor stuff here
};
// Public API - prototype
Parent.prototype = {
constructor: Parent,
func1: function () { ... },
func2: function () { ... }
}
return Parent;
}());
How do I define a child object that also uses the module pattern that inherits from parent in such a way that I can selectively override, for example, func2?

MINE.child = (function () {
var Child = function (coords) {
Parent.call(this, arguments);
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Child.prototype.func2 = function () { ... };
return Child;
}());

I find the solution from this blog (http://metaduck.com/08-module-pattern-inheritance.html) cleaner. For example:
function Parent(name) {
// Private variables here
var myName;
// Constructor
myName = name;
// Public interface
return {
func1: function () {alert("Parent func1. Name: " + myName); },
func2: function () {alert("Parent func2. Name: " + myName); }
}
}
function Child(name) {
// Private variables here
var myName,
exportObj;
// Constructor
// Inherit
exportObj = Parent(name + "'s father");
// Override
exportObj.func2 = function () {
alert("Child func2. Name: " + name);
}
// Export public interface
return exportObj;
}
An example can be run here: http://jsfiddle.net/wt4wcuLc/

Related

How to encapsulate data in javascript without function duplication in every object instance?

I create a class in JavaScript with public and private properties - data and methods to operate on this data. Some data is private and should not be accessible via "."(dot) operator from class instance. Is there way to avoid method duplication for every class instance?
function MyClass() {
let privateVar;
let publicVar;
function publicFun() {
// do something
}
function privateFun(){
// do something else
}
this.v = publicVar;
this.f = publicFun;
}
let obj1 = new MyClass();
let obj2 = new MyClass(); // publicFun and privateFun methods duplication
ClassName.prototype approach require completely public API for all class data. So this doesn't work for me.
Here is my example if I understood you correctly:
Methods are defined only once, within the wrapper function (thus they are not declared on every instance)
You can create instances of objects they will all refer to the same methods, and can have exposed data.
Here is a fiddle example:
function wrapper() {
//Methods defined only once
function method() {
alert("this is method");
}
function methodWithParams(param, callback) {
var paramsVar = param;
function realMethodHere() {
alert("We passed a param: " + paramsVar);
paramsVar = "Changed"
callback(paramsVar);
alert("Now we cahnged the param's value to: " + paramsVar + ", rerun the method to verify");
}
return realMethodHere;
}
//Class constructor
function classConstructor() {
//Private
var privateData = "Private"
function privateFunction() {
alert("this is some private function, inaccesible");
}
//This callback was addedto allow yo uto change private data.
function privateDataChangerCallback(param) {
privateData = param;
}
//Public
this.publicData = "Public"
this.callMethod = method;
this.paramMethod = methodWithParams(privateData, privateDataChangerCallback);
}
return classConstructor;
}
var classDefinition = wrapper();
var classInstance = new classDefinition();
classInstance.callMethod(); //method without param
classInstance.paramMethod(); //method with exposed Private data
//rerunning the method to see what the value is:
classInstance.paramMethod(); //method with exposed Private data
You can try using TypeScript it's a javascript library that support OOP so you can write your code like in c# or java and the compiler will generate the real javascript for you.
If I understand right, you can add a parameter to your class definition and based on this parameter, you can choose to include additional properties to your return object.
Sample
function myClass(option) {
var myFunc1 = function() {}
var myFunc2 = function() {}
var myFunc3 = function() {}
var myFunc4 = function() {}
var myFunc5 = function() {}
var finalProps = {
myFunc1: myFunc1,
myFunc2: myFunc2,
}
switch (option) {
case "all":
finalProps["myFunc5"] = myFunc5;
case "more":
finalProps["myFunc3"] = myFunc3;
finalProps["myFunc4"] = myFunc4;
break;
}
return finalProps;
}
(function() {
var f1 = new myClass();
var f2 = new myClass("more");
var f3 = new myClass("all");
console.log(f1, f2, f3)
})()
You can create a stand-alone function in the constructor function:
var HelloWorld = (function () {
function anonymouse() {
return "MUHAHAHA! ALL MINE!!!";
}
function HelloWorld() {
this.greeting = "Hello World";
}
//public
HelloWorld.prototype.greet = function () {
console.log("Hello, " + this.greeting + " " + anonymouse());
};
return HelloWorld;
}());
var greeter = new HelloWorld();
greeter.greet();
console.log(greeter);
But this does have the side effect of duplicating said function on all instances of your class.
Alternatively, maybe create a namespace to hide it in, and reference your functions from there. That would eliminate the duplicate function issue:
var MySecretClasses;
(function (MySecretClasses) {
function anonymouse() {
return "MUHAHAHA! ALL MINE!!!";
}
var HelloWorld = (function () {
function HelloWorld() {
this.greeting = "Hello World";
}
//public
HelloWorld.prototype.greet = function () {
console.log("Hello, " + this.greeting + " " + anonymouse());
};
return HelloWorld;
}());
MySecretClasses.HelloWorld = HelloWorld;
})(MySecretClasses || (MySecretClasses = {}));
var greeter = new MySecretClasses.HelloWorld();
greeter.greet();
console.log(MySecretClasses);
console.log(greeter);
TYPESCRIPT
As Shlomi Haver points out, you could use TypeScript for this.
module MySecretClasses {
function anonymouse() {
return "MUHAHAHA! ALL MINE!!!";
}
export class HelloWorld {
greeting: string = "Hello World";
constructor() {
}
//public
public greet() {
console.log("Hello, " + this.greeting + anonymouse());
}
}
}
var greeter = new MySecretClasses.HelloWorld();
greeter.greet();
console.log(greeter);

Javascript inheritance and function overriding

// Base state class -------------------------
function StateConstuctor()
{
}
// Inherited learn class --------------------
function StateLearnConstructor()
{
}
// Inherited exam class ---------------------
function StateExamConstructor()
{
}
function extend(Child, Parent)
{
var F = function() { }
F.prototype = Parent.prototype
Child.prototype = new F()
Child.prototype.constructor = Child
Child.superclass = Parent.prototype
}
function createState(rollType)
{
if (rollType == 'learn')
{
extend(StateLearnConstructor, StateConstuctor);
var state = new StateLearnConstructor();
return state;
}
else if (rollType == 'exam')
{
extend(StateExamConstructor, StateConstuctor);
var state = new StateExamConstructor();
return state;
}
}
StateConstuctor.prototype.getTitles = function()
{
console.log('base "virtual" function');
}
StateLearnConstructor.prototype.getTitles = function()
{
console.log('learn');
}
StateExamConstructor.prototype.getTitles = function()
{
console.log('exam');
}
Hello, I have the following "OOP" structure and I want to emulate something like virtual functions in C++. So I have base virtual function in StateConstructor and different realizations for each subclass.
var state = createState('exam');
state.getTitles();
But this code calls StateConstructor base virtual function. What's wrong here?
createState() is overwriting the prototypes for your StateLearnConstructor and your StateExamConstructor after you have assigned functions to them.
You shouldn't be conditionally extending them. Just extend them:
extend(StateLearnConstructor, StateConstuctor);
extend(StateExamConstructor, StateConstuctor);
StateConstuctor.prototype.getTitles = function () {
console.log('base "virtual" function');
};
StateLearnConstructor.prototype.getTitles = function () {
console.log('learn');
};
StateExamConstructor.prototype.getTitles = function () {
console.log('exam');
};
function createState(rollType) {
if (rollType == 'learn') {
return new StateLearnConstructor();
} else if (rollType == 'exam') {
return new StateExamConstructor();
}
}
Once you do that, your "virtual functions" should work as expected.
demo
Note: Your implementation for extend() is more complicated than it needs to be. The modern way to inherit a prototype is to use Object.create():
function extend(Child, Parent) {
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Child.superclass = Parent.prototype;
}

using inheritance inside a namespace [duplicate]

How do I inherit/extend classes that are using the Revealing Prototype pattern?
And is there a way to make the private variables and functions protected?
Example base object:
myNameSpace.Person = function() {
this.name= "";
this.id = 0;
};
myNameSpace.Person.prototype = function(){
var foo = function(){
//sample private function
};
var loadFromJSON = function (p_jsonObject) {
...
};
var toJSON = function () {
...
};
var clone = function (p_other) {
...
};
return {
loadFromJSON : loadFromJSON,
toJSON: toJSON,
clone: clone
};
}();
There are no protected variables/properties in JavaScript. Though, you can reuse "private" variables when you declare the inheriting classes in the same scope, which seems possible in your case when the private variables are only "hidden utilities" of your prototype.
MyNamespace.Person = function Person(params) {
// private variables and functions, individual for each Person instance
var anything, id;
function execute_something() {}
// public properties:
this.name = "";
this.getId = function getId(){
// called a "privileged function", because it has access to private variables
}
}
MyNamespace.American = function(params) {
MyNamespace.Person.call(this, params); // inherit name and getId()
}
(function() { // new scope for
// hidden utility functions and other private things
function foo() { }
function helpJSON() { }
function fromJSON() { }
var bar;
(function(personProto) { // new scope for prototype module (not explicitly needed)
// "private" /static/ variables (and functions, if you want them private)
var personCount = 0;
personProto.clone = function clone() {
return this.constructor(myself); // or something
};
personProto.toJSON = function toJSON() {
// use of helpJSON()
};
personProto.fromJSON = fromJSON; // direct use
})(MyNamespace.Person.prototype);
(function(amiProto) {
// just the same as above, if needed
amiProto.special = function() {
// use foo() and co
};
})( MyNamespace.American.prototype = Object.create(MyNamespace.Person.prototype) );
})();
This is the JavaScript way of inheritance, which means American's prototype inherits the clone(), toJSON() and fromJSON() functions automagically from the Person's prototype. Of course overwritable. And the feature is
new MyNamespace.American() instanceof MyNamespace.Person; // true
Of course, if you don't need that, and want use the more module-like way, you could reuse the utility functions, i.e. just copy them:
(function() {
// hidden utility functions and other private things
var bar;
var personCount;
function foo() { }
function helpJSON() { }
function fromJSON() { }
function clone() {
return this.constructor(myself); // or something
}
function toJSON() { }
(function(personProto) { // new scope, not really needed
// private variables are useless in here
personProto.clone = clone;
personProto.toJSON = toJSON;
personProto.fromJSON = fromJSON;
})(MyNamespace.Person.prototype);
(function(amiProto) { // new scope, not really needed
// copied from personProto
amiProto.clone = clone;
amiProto.toJSON = toJSON;
amiProto.fromJSON = fromJSON;
// and now the differences
amiProto.special = function() {
// use foo() and co
};
})(MyNamespace.American.prototype);
})();

How to implement inheritance in JS Revealing prototype pattern?

How do I inherit/extend classes that are using the Revealing Prototype pattern?
And is there a way to make the private variables and functions protected?
Example base object:
myNameSpace.Person = function() {
this.name= "";
this.id = 0;
};
myNameSpace.Person.prototype = function(){
var foo = function(){
//sample private function
};
var loadFromJSON = function (p_jsonObject) {
...
};
var toJSON = function () {
...
};
var clone = function (p_other) {
...
};
return {
loadFromJSON : loadFromJSON,
toJSON: toJSON,
clone: clone
};
}();
There are no protected variables/properties in JavaScript. Though, you can reuse "private" variables when you declare the inheriting classes in the same scope, which seems possible in your case when the private variables are only "hidden utilities" of your prototype.
MyNamespace.Person = function Person(params) {
// private variables and functions, individual for each Person instance
var anything, id;
function execute_something() {}
// public properties:
this.name = "";
this.getId = function getId(){
// called a "privileged function", because it has access to private variables
}
}
MyNamespace.American = function(params) {
MyNamespace.Person.call(this, params); // inherit name and getId()
}
(function() { // new scope for
// hidden utility functions and other private things
function foo() { }
function helpJSON() { }
function fromJSON() { }
var bar;
(function(personProto) { // new scope for prototype module (not explicitly needed)
// "private" /static/ variables (and functions, if you want them private)
var personCount = 0;
personProto.clone = function clone() {
return this.constructor(myself); // or something
};
personProto.toJSON = function toJSON() {
// use of helpJSON()
};
personProto.fromJSON = fromJSON; // direct use
})(MyNamespace.Person.prototype);
(function(amiProto) {
// just the same as above, if needed
amiProto.special = function() {
// use foo() and co
};
})( MyNamespace.American.prototype = Object.create(MyNamespace.Person.prototype) );
})();
This is the JavaScript way of inheritance, which means American's prototype inherits the clone(), toJSON() and fromJSON() functions automagically from the Person's prototype. Of course overwritable. And the feature is
new MyNamespace.American() instanceof MyNamespace.Person; // true
Of course, if you don't need that, and want use the more module-like way, you could reuse the utility functions, i.e. just copy them:
(function() {
// hidden utility functions and other private things
var bar;
var personCount;
function foo() { }
function helpJSON() { }
function fromJSON() { }
function clone() {
return this.constructor(myself); // or something
}
function toJSON() { }
(function(personProto) { // new scope, not really needed
// private variables are useless in here
personProto.clone = clone;
personProto.toJSON = toJSON;
personProto.fromJSON = fromJSON;
})(MyNamespace.Person.prototype);
(function(amiProto) { // new scope, not really needed
// copied from personProto
amiProto.clone = clone;
amiProto.toJSON = toJSON;
amiProto.fromJSON = fromJSON;
// and now the differences
amiProto.special = function() {
// use foo() and co
};
})(MyNamespace.American.prototype);
})();

Private-like properties in models or views of Backbone.js

Is it possible to have private properties in a model? Like the locally declared variables in a (constructor) function, not attached to this, but declared locally and visible only by whatever is defined in the (constructor)function.
Example without BB View:
function MyView(aModel){
var $internalInput = $('<input>');
this.render: function($where){
$internalInput.val(aModel.get('SomeProperty'));
$where.append($('<div class="inputWraper">').append($internalInput));
};
this.toggleReadonly: function() {
toggle $internalInputs readonly attribute
}
...
+ Code to bind input.val to some aModel property(ies) and setup events
...
}
Note that internalInput is not accessible to outside world and aModel is also not accessible (through MyView at least).
So if I want to use Backbone.View to implement the above MyView, how would i do it and keep $internalInput 'private'?
You should be able to achieve private data by passing an IIFE to extend when defining your Backbone objects, rather than just a plain object. For example:
var Thing = Backbone.Model.extend((function () {
var foo = "Private data!";
return {
bar: function () {
console.log(foo);
}
};
})());
You'd better off with
var Thing = Backbone.Model.extend(
{
constructor : function ()
{
var _value = "Private data!";
this.getValue = function ()
{
return _value;
};
this.setValue = function (value)
{
_value = value;
};
}
});
Javascript is fun!
var Thing = (function () {
var number_of_things = 0;
return function (options) {
var value = "Private data!";
return new ( Backbone.Model.extend({
constructor: function constructor () {
number_of_things += 1;
},
getValue: function getValue () {
return value;
}
}) )();
};
}());
I'm a little concerned by the fact that every instance of this "Thing" is also a subclass, in the OOP lingo.
In the context of using Broserify.js with Backbone (and really any above medium project) I found the following way to have private vars and functions:
myView.js
'use strict';
var config = require('../config.js'),
private_var = 'private variable',
my_private_fn = function() {
...
};
module.exports = Backbone.Model.extend({
initialize: function() {
this.my_public = 'public variable');
console.log('This is my' + this.my_public);
console.log('This is my' + my_private);
},
});
The idea to take here is go with Browserify :P
The simplest way is the following:
...
initialize:function(properites){
// Init the logic with private and public methods/variable
this.logic.initFirst(this);
// Use public methods
this.logic.doSomething();
},
logic:{
initFirst:function(modelOrView){
// Do not continue if already initiated
if( this.instance !== undefined ) return;
// Write all logic here
this.instance = (function(logic, modelOrView){
// Private variables
var private = "private";
// Public methods
logic.doSomething = function(){
console.log(private, modelOrView);
};
// Private methods
function hidden(){
}
}(this, modelOrView));
}
},

Categories