jQuery JavaScript subclassing and properties - javascript

I implement a class (I call it RowsEditor) and its subclass (I call it DateRowsEditor) with the jQuery code similar to this:
function create_RowsEditor(tableId) {
var rowsEditor = {
tableId: tableId,
quit_row: function(ctl) { /*...*/ }
};
$('#'+tableId).click(function(event) {
var tr = event.delegateTarget;
rowsEditor.quit_row(tr);
});
return rowsEditor;
}
function create_DateRowsEditor(tableId) {
var rowsEditor = $.extend({}, create_RowsEditor(tableId), {
quit_row: function(ctl) { /*...*/ }
};
return rowsEditor;
}
Then I create an object of type DateRowsEditor():
var rowsEditor = create_DateRowsEditor('come'):
The trouble is that on user's click quit_row() is called with an object created by create_RowsEditor() not with the derived object created with create_DateRowsEditor() as it should.
How to do it properly in a structured and object oriented way?

I've solved the problem by adding the .init() method:
var rowsEditor = {
tableId: tableId,
init: function() {
var self = this;
$('#'+tableId).handleRowsBlur(function(event) {
var tr = event.delegateTarget;
self.quit_row(tr);
});
return this;
},
...
and creating objects like this:
/*var rowsEditor =*/ create_ComeEditor('come').init();
It is the proper way to solve my problem because this way initialization of event handlers is called only when it is should be called.

The trouble is that on user's click quit_row() is not called with the derived object
Then don't use extend for that. Just create the object, and overwrite its quit_row method:
function create_DateRowsEditor(tableId) {
var rowsEditor = create_RowsEditor(tableId);
rowsEditor.quit_row = function(ctl) { /*...*/ };
return rowsEditor;
}

Related

Proper way to reference js property from another property

I am working on an angular project and I have a factory providing some global database methods. I tested this in a jsfiddle and it works, but I want to know if it is the right way to do it.
So here's the jsFiddle.
function DB () {
return {
newRecord: function () {
//create new record
var id = 3;
//this is the part I am wondering about
//is it ok to use this in this way??
this.setCurrentRecordId(id);
},
setCurrentRecordId: function (id) {
alert('setting id');
return localStorage.setItem('id', id);
},
getCurrentRecordId: function () {
return localStorage.getItem('id');
}
}
}
var dbStuff = new DB();
dbStuff.newRecord();
alert(dbStuff.getCurrentRecordId());
Like I said, it seems to be working; just want to know if there is a better way or not.
Thanks!
The "standard" way of using constructor functions in JavaScript would be the following:
function DB () {
this.newRecord = function () {
var id = 3;
// yes, since you invoked the DB constructor using
// using the new keyword, this will be pointing to
// the created instance
this.setCurrentRecordId(id);
};
this.setCurrentRecordId = function (id) {
alert('setting id');
return localStorage.setItem('id', id);
};
this.getCurrentRecordId = function () {
return localStorage.getItem('id');
};
}
var dbStuff = new DB();
dbStuff.newRecord();
alert(dbStuff.getCurrentRecordId());
In case you need to reference the instance in a callback or some other situation in which context is lost there are two common patterns to deal with this.
Either store a reference to this (considered "ugly" by some, very convenient though):
function Ctor(){
var self = this;
this.getSomething = function(id){
asyncThing(id).then(function(result){
// here, `this` will refer to the global object
self.doSomethingWith(result);
});
};
this.doSomethingWith = function(result){
// do something
};
}
Or use .bind() to create a new function with a predefined context:
function Ctor(){
this.getSomething = function(id){
var processResult = function(arg){
this.doSomethingWith(arg);
}.bind(this); // bind sets the function's context no matter where you'll use it
asyncThing(id).then(processResult);
};
this.doSomethingWith = function(result){
// do something
};
}
Since you are using localstorage, there isn't any problem.
function DB () {
return {
setCurrentRecordId: function (id) {
alert('setting id');
return localStorage.setItem('id', id);
},
getCurrentRecordId: function () {
return localStorage.getItem('id');
}
}
}
var dbstuff = new DB();
dbstuff.setCurrentRecordId(3);
dbstuff.getCurrentRecordId() // 3

Extend the properties returned by a function?

I'm a JS beginner. I have defined a function on my Backbone model as follows.
myFunction: function () {
return {
firstAttr: this.model.get('value-attribute')
};
}
It is available to me as this.myFunction.
From somewhere else in the code, I want to extend this.myFunction to return another attribute. In other words, I'd like it to return a dict with two attributes: { firstAttr: 'something', secondAttr: true }.
How can I do this?
I've tried:
this.myFunction().secondAttr = true;
but I know that's the wrong thing to do.
Assuming your model prototype looks like
var MyModel = Backbone.Model.extend({
myFunction: function () {
return {
// I assume you work directly on a model
// the principle would be the same with a wrapper object
firstAttr: this.get('value-attribute')
};
}
});
you can either mask your method on a model by model basis like this:
var m = new MyModel({'value-attribute': 'attr, the first'});
console.log(m.myFunction());
m.myFunction = function () {
var res = MyModel.prototype.myFunction.call(this);
res.secondAttr = true;
return res;
};
console.log(m.myFunction());
See http://jsfiddle.net/V8zt2/ for a demo
Or dynamically modify your prototype to alter all instances :
var f = MyModel.prototype.myFunction;
MyModel.prototype.myFunction = function () {
var res = f.call(this);
res.secondAttr = true;
return res;
};
var m = new MyModel({'value-attribute': 'attr, the first'});
console.log(m.myFunction());
http://jsfiddle.net/V8zt2/1/
How about modifying your myFunction to :
myFunction : function () {
var i,
obj = {};
for (i=0; i< arguments.length;i++){
obj['attribute'+(i+1)] = this.model.get(arguments[i]);
}
return obj;
}
This way you can send keys of model, that you want to be in the returned object as arguments to myFunction.

How to Properly Access Class Properties in Javascript

I have this class definition:
$.note = function() {}
$.note.prototype = {
init: function(note) {
this.note = note;
this.ctrl = document.getElementById(note);
},
// I have these getter functions because I was getting errors using
// myObject.note or myObject.ctrl
getNote: function() {
return this.note;
},
getCtrl: function() {
return this.ctrl;
}
}
I created a new object with this class like this:
var note = new $.note('C');
Which I can access in my console like this:
But when I try and access note.getNote(), I get undefined as the response:
Am I going about accessing these properties incorrectly? I've tried using just note.note or note.ctrl, and I get the same thing...
Nothing's going to call that "init" function if you don't.
$.note = function(note) { this.init(note); }
Some frameworks provide an object system that uses constructor helper functions like that, but plain JavaScript doesn't.
Try this:
$.note = function(note) { this.note = note;}
or you should call init function:
var note = new $.note();
note.init('C');

function object inheritance using jquery's extend

I'm trying to simulate inheritance using jquery extend but as far as I could test, it works only with objects.
What I'm trying to accomplish is:
var baseDefinition = function() {
var self = this;
self.calc1 = function() {
return "something1";
}
self.calc2 = function() {
return "something2";
}
self.myObject = {
propA = 100;
propB = 200;
};
}
var derivedDefinition = function() {
var self = this;
self.calc2 = function() {
return "something different from base";
}
self.myObject = {
propB = 400;
};
}
var instance = $.extend(true, {}, baseDefinition, derivedDefinition);
So I would hope to create a new instance from base definition where the derived definitions would be applied to the base ones but neither definitions would be "touched". Is it possible?
I was hoping to avoid any prototype so basicaly I would like to call instance.calc1 or instance.calc2 without knowing wether it was overriten or not.
Edit:
In my example I didn't include any object properties which was what led me to use jquery's extend function. Although both answers solve inner functions "inheritance", it doesn't (obviously) merge object properties like extend does. As a possible solution I'm thinking after I create my instance to loop through the properties of the instance and apply jquery's extend on them. Although this seems inefficient to me, I don't know if you can advise me on another course of action.
JQuery extend does not create an inheritance hierarchy, so changes you make to base definition AFTER you extend would not be reflected in derived definition. Here's how you can extend the base definition in a way that does reflect later changes down the inheritance hierarchy using Javascript prototypal inheritance:
var baseDefinition = function() {};
baseDefinition.prototype.calc1 = function() {
return "something1";
};
baseDefinition.prototype.calc2 = function() {
return "something2";
};
var derivedDefinition = function() {};
derivedDefinition.prototype = Object.create(baseDefinition.prototype);
derivedDefinition.prototype.calc2 = function() {
return "something different from base";
};
var instance = new derivedDefinition();
instance.calc1(); // something1
instance.calc2(); // something different from base
$.extend only works on already existing objects, not on function which will instantiate objects in the (far?) future:
var instance = $.extend(true, {}, new baseDefinition(), new derivedDefinition());
However, you could of course design an extend function that works for constructors and that returns a function:
function extendConstr() {
var fns = arguments;
return function newConstr(){
var self = {};
for (var i=0; i<fns.length; i++)
fns[i].apply(self, arguments);
return self;
}
}
var extendedFunction = extendConstr(baseDefinition, derivedDefinition);
var instance = extendedFunction();
console.log(instance); // has `calc1` and overwritten `calc2`
Btw, without an extend function you could've done that already manually in the derived constructor:
function derivedDefinition() {
baseDefinition.call(this/*, arguments */);
this.calc2 = function() {
return "something different from base";
}
}
console.log(new derivedDefinition) // has a `calc1` as well

Is it possible to append functions to a JS class that have access to the class's private variables?

I have an existing class I need to convert so I can append functions like my_class.prototype.my_funcs.afucntion = function(){ alert(private_var);} after the main object definition. What's the best/easiest method for converting an existing class to use this method? Currently I have a JavaScript object constructed like this:
var my_class = function (){
var private_var = '';
var private_int = 0
var private_var2 = '';
[...]
var private_func1 = function(id) {
return document.getElementById(id);
};
var private_func2 = function(id) {
alert(id);
};
return{
public_func1: function(){
},
my_funcs: {
do_this: function{
},
do_that: function(){
}
}
}
}();
Unfortunately, currently, I need to dynamically add functions and methods to this object with PHP based on user selected settings, there could be no functions added or 50. This is making adding features very complicated because to add a my_class.my_funcs.afunction(); function, I have to add a PHP call inside the JS file so it can access the private variables, and it just makes everything so messy.
I want to be able to use the prototype method so I can clean out all of the PHP calls inside the main JS file.
Try declaring your "Class" like this:
var MyClass = function () {
// Private variables and functions
var privateVar = '',
privateNum = 0,
privateVar2 = '',
privateFn = function (arg) {
return arg + privateNum;
};
// Public variables and functions
this.publicVar = '';
this.publicNum = 0;
this.publicVar2 = '';
this.publicFn = function () {
return 'foo';
};
this.publicObject = {
'property': 'value',
'fn': function () {
return 'bar';
}
};
};
You can augment this object by adding properties to its prototype (but they won't be accessible unless you create an instance of this class)
MyClass.prototype.aFunction = function (arg1, arg2) {
return arg1 + arg2 + this.publicNum;
// Has access to public members of the current instance
};
Helpful?
Edit: Make sure you create an instance of MyClass or nothing will work properly.
// Correct
var instance = new MyClass();
instance.publicFn(); //-> 'foo'
// Incorrect
MyClass.publicFn(); //-> TypeError
Okay, so the way you're constructing a class is different than what I usually do, but I was able to get the below working:
var my_class = function() {
var fn = function() {
this.do_this = function() { alert("do this"); }
this.do_that = function() { alert("do that"); }
}
return {
public_func1: function() { alert("public func1"); },
fn: fn,
my_funcs: new fn()
}
}
var instance = new my_class();
instance.fn.prototype.do_something_else = function() {
alert("doing something else");
}
instance.my_funcs.do_something_else();
As to what's happening [Edited]:
I changed your my_funcs object to a private method 'fn'
I passed a reference to it to a similar name 'fn' in the return object instance so that you can prototype it.
I made my_funcs an instance of the private member fn so that it will be able to execute all of the fn methods
Hope it helps, - Kevin
Maybe I'm missing what it is you're trying to do, but can't you just assign the prototype to the instance once you create it? So, first create your prototype object:
proto = function(){
var proto_func = function() {
return 'new proto func';
};
return {proto_func: proto_func};
}();
Then use it:
instance = new my_class();
instance.prototype = proto;
alert(instance.prototype.proto_func());

Categories

Resources