How to Implement Inheritance in Js Revealing Prototype Pattern

How to implement inheritance in JS Revealing prototype pattern?

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);
})();

Creating inheritance on revealing modular pattern objects

$.extend(this, foo);

this is not the object which you return from the function below (in fact it cannot be since it's created after this call), but the global object - check MDN's introduction to the this keyword.

For what you want to do, there are two ways:

  • Copy all properties from foo onto your bar object after it is created:

    var bar = (function() {

    return {…};
    })();
    $.extend(bar, foo);

    You can do that as well directly on the returned object:

        return $.extend({…}, foo);

    A variant of this pattern allows you to overwrite foo properties. Copy foo into an empty object, then write your bar properties to it:

        return $.extend({}, foo, {…});
  • Use prototypical inheritance. Create an object that inherits its properties from foo, and then write your bar properties to it:

        return $.extend(Object.create(foo), {…});

    Now when foo changes afterward, you still can access those new properties on bar (unless they're shadowed by own properties). Notice that Object.create might not be supported in legacy environments, but you can easily shim it.


As noted by @raina77ow, your doBarStuff function is flawed too. The doFooStuff function is not in the scope of your function, and you cannot change that. You never will be able to access the private declared functions and variables from the foo module, only those that are public - if you did need them, consider a different pattern or app design. However, doFooStuff is a property on the exported (public) foo object, from which bar inherits (regardless in which of the above demonstrated ways). Therefore, you can access it as a method of bar as well, usually by using this.doFooStuff().

Can a revealing prototype pattern extent another revealing prototype pattern

Am I missing anything?

Yes.

new function(proto) {

Do not use new.

new base().eat (data);//This is just to show you can call the base. If needed.

Do not use new. You'd create a new instance here, however what you want is get the eat method from the base.prototype and call that on your Fish instance.

proto = new Animal()

Do not use new!


Fish.prototype = (function(super) {
var proto = Object.create(super);

function eat(data) {
super.eat.call(this, data);
document.write("fish eating.<br>");
}
function swim(){
document.write("fish is swimming.<br>");
}

proto.constructor = Fish;
proto.eat = eat;
proto.swim = swim;

return proto;
})(Animal.prototype);

Revealing Prototype Pattern private variables

There is not a way to do that with the revealing prototype pattern.

You can only do that with something like this:

function MyClass() {
var v = 1;
this.getV = function() {
return v;
};
}

And that's why there are some die-hard enthusiasts for this kind of approach.

Personal option: Stick an underscore in front of it, and putting it on the object: this._v. Don't fight JavaScript; use it.

Inheritance and module pattern

You're not using the module pattern in the correct way. Somehow, your "constructor" is called as an immediately-invoked function expression (IIFE), and the module closure is not. It should be the other way round.

Also, you can't assign to this.prototype. To create the prototype object from which all instances will inherit, you will need to assign to the prototype property of the constructor function (the this keyword even pointed to the global window object in your case).

And you should return the constructor function, not the prototype object, from the IIFE as soon as you have it.

Parent = (function () {
// constructor
function construct () {
console.log("Parent");
};

// public functions
construct.prototype.test = function () {
console.log("test parent");
};
construct.prototype.test2 = function () {
console.log("test2 parent");
};

return construct;
})();

Child = (function () {
// constructor
function construct() {
console.log("Child");
Parent.apply(this, arguments);
}

// make the prototype object inherit from the Parent's one
construct.prototype = Object.create(Parent.prototype);
// public functions
construct.prototype.test = function() {
console.log("test Child");
};

return construct;
})();

Revealing Module / Prototype Pattern

Although this code works as expected I red an article lately that this pattern uses large amount of memory if you have multiple instances

The code you presented in your first snippet is a singleton module, there are no "multiple instances". It is completely fine.

Only the thing that you titled Module pattern - Multiple instances in your fiddle does suffer from memory disadvantages when it instantiates a very large amount of objects. However, notice that this is not a "module pattern" but the "factory pattern".

Now I'm wondering if it's faster / better for memory to use the Revealing Prototype Pattern?

In general, and if applied correctly, yes.

This benchmark surprised me, because the Module Pattern seems to be much faster. Is there any reason?

The code of their modules is messed up beyond all repair. I'm not even trying to comment on what is going on there.

Also I couldn't figure out how to have multiple instances with the Revealing Prototype Pattern

The benefit of the prototype is that its properties are shared amongst all instances. This means that the .init method of all instances points to the same function, which has a single privateVar in its revealing module scope - this variable exists only once for all instances! It's static, not instance-specific.

If you want to use the prototype, you cannot access truly private variables; you will need to use public properties. However, your clickFunction needs a local (private) variable anyway for its closure, so there would be nothing wrong with not using the prototype here:

function Constructor( el ) {
var privateVar = $( el );
privateVar.on( 'click', function clickFunction() {
privateVar.addClass('click');
});

console.log( 'constructor: ' + privateVar.attr('id') );
}


Related Topics



Leave a reply



Submit