Javascript class extension - javascript

CONTEXT
I have one Base class called Entity and User and Group are both derived from this object; If I instantiate a user with the ID of 12 for example, the next time I try to do that it returns the same object. I store these in a prototype items variable. To keep these item variables separate I have to declare the User And Group functions separately although they contain the same code.
CODE
Application.prototype.Entity = function() {
};
Application.prototype.Entity.prototype.print = function() {
var container = $("<div class='user-tag'></div>");
container.append("<a class='friend_list ellipsis_overflow' style='background-image:url(\"" + this.entity.pic.icon + "\");'"
+ "href ='user?id=" + this.entity.id + "'>" + this.entity.name + "</a>");
return container;
};
//HOW TO GET RID OF THIS "REPETITION"
Application.prototype.User = function(entity) {
this.entity = entity;
this.entity.pic = this.entity.pic || Application.prototype.default.pic;
if (this.items[this.entity.id]) {
return this.items[this.entity.id];
} else {
this.items[this.entity.id] = this;
}
};
Application.prototype.Group = function(entity) {
this.entity = entity;
this.entity.pic = this.entity.pic || Application.prototype.default.pic;
if (this.items[this.entity.id]) {
return this.items[this.entity.id];
} else {
this.items[this.entity.id] = this;
}
};
// END REPEAT
Application.prototype.Group.prototype = new Application.prototype.Entity();
Application.prototype.User.prototype = new Application.prototype.Entity();
//Application.prototype.User.prototype.constructor = Application.prototype.Entity;
//Application.prototype.Group.prototype.constructor = Application.prototype.Entity; - these don't seem to work
//THESE items VARIABLES HAVE TO REMAIN SEPARATE
Application.prototype.Group.prototype.items = {};
Application.prototype.User.prototype.items = {};
QUESTION
I specifically would like to rid my code of the repetition mentioned above, but if you see any other unnecessary code, please comment. Thanks!

Something like this?
function userAndGroupConstructor(entity) {
this.entity = entity;
this.entity.pic = this.entity.pic || Application.prototype.default.pic;
if (this.items[this.entity.id]) {
return this.items[this.entity.id];
} else {
this.items[this.entity.id] = this;
}
}
Application.prototype.User = function() {
return userAndGroupConstructor.apply(this, arguments)
}
Application.prototype.Group = function() {
return userAndGroupConstructor.apply(this, arguments)
}
You get distinct constructors with distinct prototypes, but avoid duplication.

You can do this:
Application.prototype.Group = Application.prototype.User;
Since Application.prototype.User contains the function reference, you can just assign it to Application.prototype.Group.

Related

How to expose only a function and not the variables from a class function?

Suppose I have this code:
function GraphFactory() {
this.nodeNames = [];
this.pinnedNodes = [];
this.initPinnedNodes = function(nodes) {
if (nodes) {
this.pinnedNodes = nodes;
} else {
this.pinnedNodes = [];
}
}
this.checkIfPinned = function(node) {
if (this.pinnedNodes.indexOf(node) > -1) {
return true;
} else {
return false;
}
}
this.addToPinnedNodes = function(name) {
this.pinnedNodes.push(name);
return true;
}
this.removeFromPinnedNodes = function(name) {
this.pinnedNodes.splice(this.pinnedNodes.indexOf(name), 1);
return true;
}
}
let graphFactory = new GraphFactory();
Right now i can access both the function
graphFactory.checkIfPinned(label);
but also directly the variable
graphFactory.pinnedNodes
How would I set things up so only the functions, but not the variables could get accessed?
Use variables instead of properties on the object.
let nodeNames = [];
They'll be closed over by your dynamically assigned instance methods.
Or see this question for the modern approach.
With the current construct, you can use a closure per object, practically a local variable in your constructor function:
function GraphFactory() {
var nodeNames = [];
var pinnedNodes = [];
this.initPinnedNodes = function(nodes) {
if (nodes) {
pinnedNodes = nodes;
} else {
pinnedNodes = [];
}
}
this.checkIfPinned = function(node) {
if (pinnedNodes.indexOf(node) > -1) {
return true;
} else {
return false;
}
}
this.addToPinnedNodes = function(name) {
pinnedNodes.push(name);
return true;
}
this.removeFromPinnedNodes = function(name) {
pinnedNodes.splice(this.pinnedNodes.indexOf(name), 1);
return true;
}
}
let graphFactory = new GraphFactory();
However such variables will not be accessible to methods you add later, from outside. For example that nodeNames exists in vain this way.

Javascript: Dynamically created functions in a given object issue

While this question have been asked before and many already answered it, my question is strictly about the prototype of the newly created functions.
If you read this piece of code, you will understand that it just works. Also here on codepen.
// main object
var Foo = {};
// main methods
Foo.render = {}; // the Render function to populate later
Foo.start = function(el,ops){
return new Actions(el,ops);
}
// secondary/utility functions
var Actions = function(el,ops){
this.el = document.querySelector(el);
this.ops = ops || {};
this.prepare(); // this builds the Foo.render functions
for (var p in this.ops){
Foo.render[p](this);
}
};
// Action methods
Actions.prototype.prepare = function(){
for (var p in this.ops) {
Foo.render[p] = function(that){ // or r[p]
that.el.style[p] = that.ops[p] + 'px';
}
}
}
// init
var action = new Foo.start('div',{left:15})
// check
console.log(Foo.render['left'].prototype);
<div></div>
The problem is the prototype of the newly created function Foo.render['left'] is something like this Foo.render.(anonymous function) {} instead of something like Foo.render.left() {} or something else, and I am experiencing some performance loss because I am unable to access the newly created function's prototype very fast.
Can anyone please shed some light on how to adapt the .prepare() function to create accurate/accessible (I can't chose the right word) prototype functions within the Foo scope?
Thank you.
You will need to capture the value of p in an extra closure scope. Also I would recommend avoiding to overwrite already-existing methods.
Actions.prototype.prepare = function() {
for (var p in this.ops) {
if (!(p in Foo.render)) {
Foo.render[p] = (function(prop) {
return function(that) {
that.el.style[prop] = that.ops[prop] + 'px';
};
}(p));
}
}
};
or
Actions.prototype.prepare = function() {
for (var p in this.ops) {
(function() {
var prop = p;
if (!(prop in Foo.render)) {
Foo.render[prop] = function(that) {
that.el.style[prop] = that.ops[prop] + 'px';
};
}
}());
}
}
I think I found a way to make it work better. The following should do, however I'm still curious to know if there is any better solution.
// main object
var Foo = {};
// main methods
Foo.render = {}; // the Render function to populate later
Foo.start = function(el,ops){
return new Actions(el,ops);
}
// secondary/utility functions
var Actions = function(el,ops){
this.el = document.querySelector(el);
this.ops = ops || {};
this.prepare(); // this builds the Foo.render functions
for (var p in this.ops){
Foo.render[p](this);
}
};
// Action methods
Actions.prototype.prepare = function(){
for (var p in this.ops) {
Foo.render[p] = (function(){ // or r[p]
return function(that){
that.el.style[p] = that.ops[p] + 'px';
}
})();
}
};
// init
var action = new Foo.start('div',{left:15})
// check
console.log(Foo.render['left'].prototype);
UPDATE: I think I found a way to eliminate one of the closures, basically using the p as the function's second attribute, like so Foo.render[p] = function(that,p){} here we go:
// main object
var Foo = {};
// main methods
Foo.render = {}; // the Render function to populate later
Foo.start = function(el,ops){
return new Actions(el,ops);
}
// secondary/utility functions
var Actions = function(el,ops){
this.el = document.querySelector(el);
this.ops = ops || {};
this.prepare(); // this builds the Foo.render functions
for (var p in this.ops){
Foo.render[p](this,p); // we include p here
}
};
// Action methods
Actions.prototype.prepare = function(){
for (var p in this.ops) {
Foo.render[p] = function(that,p){ // we also include p here
that.el.style[p] = that.ops[p] + 'px';
};
}
};
// init
var action = new Foo.start('div',{left:15})
// check
console.log(Foo.render['left'].prototype);
<div></div>
This eliminates the additional closure and brings the function closer to the main thread's scope. Any comment on the update is welcome.

2 instances of a module pattern using a constructor function as the return value of the module

Here is a jsFiddle: http://jsfiddle.net/hb2pE/
I have a constructor function inside a module pattern. I create 2 instances of this module using this code:
var jo = new sliderJS("mySliderJS")
jo.start()
var jow = new sliderJS("mySliderJS2")
jow.start()
This is the module sliderJS:
var sliderJS = function($){
var sliderJS,
sliderJSslide,
sliderJSslideLength,
sliderJSprev,
sliderJSnext,
slideEvents,
sliderID
slideEvents = {
doSlide: function(type, e){
if(e){e.preventDefault()}
for(var i = 0; i < sliderJSslideLength; i++){
var slideIsActive = hasClass("active", sliderJSslide[i])
if(slideIsActive){
sliderJSslide[i].className = "sliderJS-slide"
switch (type) {
case "next":
slideEvents.moveSlideNext(i)
break;
case "prev":
slideEvents.moveSlidePrev(i)
break;
}
break;
}
}
},
moveSlideNext: function(i){
if(i+1 < sliderJSslide.length){
sliderJSslide[i+1].className = "sliderJS-slide active"
} else {
sliderJSslide[0].className = "sliderJS-slide active"
}
},
moveSlidePrev: function(i){
if(i-1 != -1){
sliderJSslide[i-1].className = "sliderJS-slide active"
} else {
sliderJSslide[sliderJSslide.length -1].className = "sliderJS-slide active"
}
}
}
function start(id){
setVariables(id)
setEventListeners()
}
function setVariables(id){
//sliderID = id
sliderJS = $("#" + sliderID)[0]
sliderJSslide = $("#" + sliderID + " > .sliderJS-slide")
sliderJSslideLength = sliderJSslide.length
sliderJSprev = $("#" + sliderID + "-prev")[0]
sliderJSnext = $("#" + sliderID + "-next")[0]
}
function setEventListeners(){
sliderJSnext.addEventListener("click", function(){return function(e){ slideEvents.doSlide("next", e)}}())
sliderJSprev.addEventListener("click", function(){return function(e){ slideEvents.doSlide("prev", e)}}())
}
function hasClass(parClass, parElement){
if(parElement.className.indexOf(parClass) == -1){return false} else {return true}
}
return function constr(id){
sliderID = id
this.start = start
}
}(Sizzle)
If i'm using only 1 instance than the code works. As soon as i create a second instance the slider for the first one stops working because all the variables values have been changed to reflect the second slider.
What am i doing wrong?
Here is that jsFiddle again (remove the last instance -var jow- to see the script actually working): http://jsfiddle.net/hb2pE/
The module is a little bit messed up. It should follow this pattern:
var sliderJS = function(sliderID) {
//define private functions here eg. setEventListeners
//Return an object containing functions that are public eg. doSlide
return {
doSlide: function(type, e){
}
};
};
So you don't need the new keyword, just call:
var slider1 = sliderJS("mySliderJS2");
If, however, you're creating a lot of instances and would prefer better performance, you'd be better off creating a function and populating its prototype - then you can call that function with the new keyword.

Is there any method to change the priority of the two assignment methods?

This is an interview questions, Asked to write a Man class to let the following code can run properly:
var me =new Man();
//method one:
me.attr("fullname", "tom");
//method two:
me.fullname = "jim";
console.info("my name is:" + me.attr("fullname"));
/*------[result is]------
my name is:tom
------------------*/
my answer is:
var Man=function(){
};
Man.prototype.attr=function(attr,val){
if(val){
this[attr]=val;
}
else{
return this[attr];
}
}
The results of my code to run is:
/*------[result is]------
my name is:jim
------------------*/
who can help me? thanks
You could make a second dictionary:
function Man() {
this.dictionary = {};
}
Man.prototype.attr = function(attr, val) {
if(arguments.length > 1) {
this.dictionary[attr] = val;
} else {
return this.dictionary[attr];
}
};
You could use a closed variable:
function Man() {
var dictionary = {};
this.attr = function(attr, val) {
if(arguments.length > 1) {
dictionary[attr] = val;
} else {
return dictionary[attr];
}
};
}
You could use a closed fullname variable, and ignore everything except 'fullname':
function Man() {
var fullname;
this.attr = function(attr, val) {
if(attr === 'fullname') {
if(arguments.length > 1) {
fullname = val;
} else {
return fullname;
}
}
};
}
You could also return "tom" every single time, or pretend all the attributes are "fullname", or both. You could ROT13 property names before assigning them. You could add underscores. You could create a property instead that throws away values beginning with "j". The possibilities are limitless, actually.
Use a property to save attributes.
var Man=function(){
this.attributes = {};
};
Man.prototype.attr=function(attr,val){
if(val){
this.attributes[attr] = val;
}
else{
return this.attributes[attr];
}
}

Super in Backbone

When I override the clone() method of a Backbone.Model, is there a way to call this overriden method from my implantation? Something like this:
var MyModel = Backbone.Model.extend({
clone: function(){
super.clone();//calling the original clone method
}
})
You'll want to use:
Backbone.Model.prototype.clone.call(this);
This will call the original clone() method from Backbone.Model with the context of this(The current model).
From Backbone docs:
Brief aside on super: JavaScript does not provide a simple way to call
super — the function of the same name defined higher on the prototype
chain. If you override a core function like set, or save, and you want
to invoke the parent object's implementation, you'll have to
explicitly call it.
var Note = Backbone.Model.extend({
set: function(attributes, options) {
Backbone.Model.prototype.set.apply(this, arguments);
...
}
});
You can also use the __super__ property which is a reference to the parent class prototype:
var MyModel = Backbone.Model.extend({
clone: function(){
MyModel.__super__.clone.call(this);
}
});
Josh Nielsen found an elegant solution for this, which hides a lot of the ugliness.
Just add this snippet to your app to extend Backbone's model:
Backbone.Model.prototype._super = function(funcName){
return this.constructor.prototype[funcName].apply(this, _.rest(arguments));
}
Then use it like this:
Model = Backbone.model.extend({
set: function(arg){
// your code here
// call the super class function
this._super('set', arg);
}
});
Working from the answers given by geek_dave and charlysisto, I wrote this to add this._super(funcName, ...) support in classes that have multiple levels of inheritance. It's worked well in my code.
Backbone.View.prototype._super = Backbone.Model.prototype._super = function(funcName) {
// Find the scope of the caller.
var scope = null;
var scan = this.__proto__;
search: while (scope == null && scan != null) {
var names = Object.getOwnPropertyNames(scan);
for (var i = 0; i < names.length; i++) {
if (scan[names[i]] === arguments.callee.caller) {
scope = scan;
break search;
}
}
scan = scan.constructor.__super__;
}
return scan.constructor.__super__[funcName].apply(this, _.rest(arguments));
};
A year later I've fixed some bugs and made things faster. Below is the code that I use now.
var superCache = {};
// Hack "super" functionality into backbone.
Backbone.View.prototype._superFn = Backbone.Model.prototype._superFn = function(funcName, _caller) {
var caller = _caller == null ? arguments.callee.caller : _caller;
// Find the scope of the caller.
var scope = null;
var scan = this.__proto__;
var className = scan.constructor.className;
if (className != null) {
var result = superCache[className + ":" + funcName];
if (result != null) {
for (var i = 0; i < result.length; i++) {
if (result[i].caller === caller) {
return result[i].fn;
}
}
}
}
search: while (scope == null && scan != null) {
var names = Object.getOwnPropertyNames(scan);
for (var i = 0; i < names.length; i++) {
if (scan[names[i]] === caller) {
scope = scan;
break search;
}
}
scan = scan.constructor.__super__;
}
var result = scan.constructor.__super__[funcName];
if (className != null) {
var entry = superCache[className + ":" + funcName];
if (entry == null) {
entry = [];
superCache[className + ":" + funcName] = entry;
}
entry.push({
caller: caller,
fn: result
});
}
return result;
};
Backbone.View.prototype._super = Backbone.Model.prototype._super = function(funcName) {
var args = new Array(arguments.length - 1);
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i + 1];
}
return this._superFn(funcName, arguments.callee.caller).apply(this, args);
};
Then given this code:
var A = Backbone.Model.extend({
// className: "A",
go1: function() { console.log("A1"); },
go2: function() { console.log("A2"); },
});
var B = A.extend({
// className: "B",
go2: function() { this._super("go2"); console.log("B2"); },
});
var C = B.extend({
// className: "C",
go1: function() { this._super("go1"); console.log("C1"); },
go2: function() { this._super("go2"); console.log("C2"); }
});
var c = new C();
c.go1();
c.go2();
The output in the console is this:
A1
C1
A2
B2
C2
What's interesting is that class C's call to this._super("go1") scans the class hierarchy until it gets a hit in class A. Other solutions do not do this.
P.S. Uncomment the className entries of the class definitions to enable caching of the _super lookup. (The assumption is that these class names will be unique in the application.)
If you want just to call this._super(); without passing the function name as an argument
Backbone.Controller.prototype._super = function(){
var fn = Backbone.Controller.prototype._super.caller, funcName;
$.each(this, function (propName, prop) {
if (prop == fn) {
funcName = propName;
}
});
return this.constructor.__super__[funcName].apply(this, _.rest(arguments));
}
Better use this plugin:
https://github.com/lukasolson/Backbone-Super
I believe you can cache the original method (although not tested):
var MyModel = Backbone.Model.extend({
origclone: Backbone.Model.clone,
clone: function(){
origclone();//calling the original clone method
}
});
backbone._super.js, from my gists: https://gist.github.com/sarink/a3cf3f08c17691395edf
// Forked/modified from: https://gist.github.com/maxbrunsfeld/1542120
// This method gives you an easier way of calling super when you're using Backbone in plain javascript.
// It lets you avoid writing the constructor's name multiple times.
// You still have to specify the name of the method.
//
// So, instead of having to write:
//
// var Animal = Backbone.Model.extend({
// word: "",
// say: function() {
// return "I say " + this.word;
// }
// });
// var Cow = Animal.extend({
// word: "moo",
// say: function() {
// return Animal.prototype.say.apply(this, arguments) + "!!!"
// }
// });
//
//
// You get to write:
//
// var Animal = Backbone.Model.extend({
// word: "",
// say: function() {
// return "I say " + this.word;
// }
// });
// var Cow = Animal.extend({
// word: "moo",
// say: function() {
// return this._super("say", arguments) + "!!!"
// }
// });
(function(root, factory) {
if (typeof define === "function" && define.amd) {
define(["underscore", "backbone"], function(_, Backbone) {
return factory(_, Backbone);
});
}
else if (typeof exports !== "undefined") {
var _ = require("underscore");
var Backbone = require("backbone");
module.exports = factory(_, Backbone);
}
else {
factory(root._, root.Backbone);
}
}(this, function(_, Backbone) {
"use strict";
// Finds the next object up the prototype chain that has a different implementation of the method.
var findSuper = function(methodName, childObject) {
var object = childObject;
while (object[methodName] === childObject[methodName]) {
object = object.constructor.__super__;
}
return object;
};
var _super = function(methodName) {
// Keep track of how far up the prototype chain we have traversed, in order to handle nested calls to `_super`.
this.__superCallObjects__ || (this.__superCallObjects__ = {});
var currentObject = this.__superCallObjects__[methodName] || this;
var parentObject = findSuper(methodName, currentObject);
this.__superCallObjects__[methodName] = parentObject;
// If `methodName` is a function, call it with `this` as the context and `args` as the arguments, if it's an object, simply return it.
var args = _.tail(arguments);
var result = (_.isFunction(parentObject[methodName])) ? parentObject[methodName].apply(this, args) : parentObject[methodName];
delete this.__superCallObjects__[methodName];
return result;
};
// Mix in to Backbone classes
_.each(["Model", "Collection", "View", "Router"], function(klass) {
Backbone[klass].prototype._super = _super;
});
return Backbone;
}));
In the case that you don't know what the parent class is exactly (multiple inheritance or you want a helper function) then you can use the following:
var ChildModel = ParentModel.extend({
initialize: function() {
this.__proto__.constructor.__super__.initialize.apply(this, arguments);
// Do child model initialization.
}
});
With helper function:
function parent(instance) {
return instance.__proto__.constructor.__super__;
};
var ChildModel = ParentModel.extend({
initialize: function() {
parent(this).initialize.apply(this, arguments);
// Do child model initialization.
}
});
Pass the parent class as an option during instantiation:
BaseModel = Backbone.Model.extend({
initialize: function(attributes, options) {
var self = this;
this.myModel = new MyModel({parent: self});
}
});
Then in your MyModel you can call parent methods like this
this.options.parent.method();
Keep in mind this creates a retain cycle on the two objects. So to let the garbage collector do it's job you would need to manually destroy the retain on one of the objects when finished with it. If you're application is pretty large. I would encourage you to look more into hierarchal setups so events can travel up to the correct object.
2 functions below, one requires you pass in the function name, the other can "discover" which function we want the super version of
Discover.Model = Backbone.Model.extend({
_super:function(func) {
var proto = this.constructor.__super__;
if (_.isUndefined(proto[func])) {
throw "Invalid super method: " + func + " does not exist in prototype chain.";
}
return proto[func].apply(this, _.rest(arguments));
},
_superElegant:function() {
t = arguments;
var proto = this.constructor.__super__;
var name;
for (name in this) {
if (this[name] === arguments.callee.caller) {
console.log("FOUND IT " + name);
break;
} else {
console.log("NOT IT " + name);
}
}
if (_.isUndefined(proto[name])) {
throw "Super method for: " + name + " does not exist.";
} else {
console.log("Super method for: " + name + " does exist!");
}
return proto[name].apply(this, arguments);
},
});
Here is how I would do this:
ParentClassName.prototype.MethodToInvokeName.apply(this);
so for your example this is:
Model.prototype.clone.apply(this)

Categories