Re-initialize a javascript function object by variable - javascript

This one is something that is fairly easy to do in PHP and I find my self in a situation where it would come in handy, but I do not believe the PHP trick will work.
Basically I want to use a variable passed from a function within an object to then reinitialize that object using the child (defined by the variable).
var View = function(){
var fn = this;
fn.load = function(name){
return new name();
}
}
var view = View.load('titleView');
This is a very early work on it, so forgive the fact that it looks so strange (still need to tinker more with the concept). But overall it should roughly show the concept.
Is there a way to basically recreate the current functions instance with a new function? To do this in the aspect I am thinking of I will need to use a variable rather then pass the new object. Is this possible? I am sure in some form it has to be. Any ideas/pointers? Google has been failing me since I am not sure of the right keywords for this.
EDIT:
should also show the idea behind the "titleView" class
var titleView = function(){}
titleView.prototype = new View;

I think the easiest way to do this is via some kind of factory that can produce the types of views you are wanting. Something like this:
var View = (function() {
var registry = {};
return {
register: function(type, fn) {
if (typeof registry[type] === 'undefined') {
registry[type] = fn;
return true;
}
return false;
},
load: function(type) {
return new registry[type]();
}
};
})();
var titleView = function() {
this.name = 'titleView';
};
var subTitleView = function() {
this.name = 'subTitleView';
}
View.register('titleView', titleView);
View.register('subTitleView', subTitleView);
var view = View.load('titleView');
console.log("Created view type: " + view.name);
view = View.load('subTitleView');
console.log("Created view type: " + view.name);
This would give the following (allowing you to recreate view variable on the fly):
// Created view type: titleView
// Created view type: subTitleView
If you try to do it the way your example is going, you'll have to use subclasses like so:
function Base() {
this.name = "Base";
this.load = function(fn) {
fn.apply(this);
}
}
function Other() {
Base.apply(this, arguments);
this.name = "Other";
}
var view = new Base();
console.log(view);
view.load(Other);
console.log(view);
// => Base { name: "Base", load: function }
// => Base { name: "Other", load: function }
However, with this method, after calling view.load(Other), your view will still retain whatever properties/methods it had prior to calling load (which may not be what you want).

think this is what ur asking
var View = function(){
this.load = function(name){
return name;
}
}
var myView = new View;
var v = myView.load('titleView');
alert(v);

Related

Accessing variables in the parent class

I've been stumped on this problem for a few hours now, and I can't seem to find a solution. I have a class that extends a parent class, but I cannot seem to access the variables declared in the parent's constructor.
I borrowed my inheritance technique. It simply uses an "extend" function to create the subclass:
//In functions.js
function extend(base, sub, methods) {
sub.prototype = Object.create(sub.prototype);
sub.prototype.constructor = sub;
sub.base = base.prototype;
for(var name in methods) { sub.prototype[name] = methods[name]; }
return sub;
}
I create a class called Stimulus that serves as a function:
//In classes.js
function Stimulus(module_id, unit_id, attributes) {
this.attributes = attributes;
this.module_id = module_id;
this.unit_id = unit_id;
//create some other class variables based on this.attributes, this.module_id, and this.unit_id
}
Stimulus.prototype = {
_getStimulus: function() { //retrieve from database }
//other functions here
}
And finally I have the subclass. The technique I use to create it is also borrowed from the above link:
//In classes.js
ImageStimulus = (function() {
var $this = function(module_id, unit_id, attributes) {
$this.base.constructor.call(this, module_id, unit_id, attributes);
};
extend(Stimulus, $this, {
initialize: function() {
this.fixation_cross = this.attributes['Fixation Cross'] ? this.attributes['Fixation Cross'] : false;
//do other stuff
}
//other functions here
});
return $this;
})();
It all seems straightforward enough. However, in my main script, when I try to run this, I create the object and then try to run the initialize() function and everything falls apart:
//In main.js
var stimulus_objects = [];
for(var i = 0; i < someLimit; i++) {
//module_id is passed directly to this function
var unit_id = //some source;
var stimulus_attributes = //some source;
stimulus_objects[i] = new ImageStimulus(module_id, unit_id, stimulus_attributes);
stimulus_objects[i].initialize();
}
If I check the console I see that is says
Uncaught TypeError: Cannot read property 'Fixation Cross' of undefined
And it corresponds to the line in ImageStimulus.initialize() where I try to call on this.attributes['Fixation Cross'].
It seems that something is going wrong in making Stimulus the prototype of ImageStimulus, because ImageStimulus.initialize() cannot access the this.attributes variables that is created in the constructor for the Stimulus class.
Does anybody else see the error?
I have a decent amount of OOP programming in Java, C++, and even PHP, but this is my first attempt at JavaScript OOP, and so I feel like I'm probably making some simple mistake.
EDIT: Solved the problem... somehow.
So it seems there was a fairly trivial solution. The Stimulus function was never being called, and it should have been called on the line with $this.base.constructor.call(). In the Stimulus.prototype object I added constructor: Stimulus and now Stimulus is being called properly. It seems odd that I had to do that (shouldn't Stimulus() be it's own constructor?), but it works!
Stimulus.prototype = {
constructor: Stimulus,
_getStimulus: function() {...
Does anyone know why that occurred and why my fix worked? I'm trying to understand what I did.
I get it. You're replacing the entire prototype, so it is killing the constructor. By setting the constructor explicitly, you are putting it back. The alternative is to set the prototype method directly rather than setting the entire prototype.
For this fiddle to be useful, bring up the console and set a break point before clicking Run.
http://jsfiddle.net/x2v7wv6j/
//In functions.js
function extend(base, sub, methods) {
sub.prototype = Object.create(sub.prototype);
sub.prototype.constructor = sub;
sub.base = base.prototype;
for(var name in methods) { sub.prototype[name] = methods[name]; }
return sub;
}
//In classes.js
function Stimulus(module_id, unit_id, attributes) {
this.attributes = attributes;
this.module_id = module_id;
this.unit_id = unit_id;
//create some other class variables based on this.attributes, this.module_id, and this.unit_id
}
Stimulus.prototype._getStimulus = function() { //retrieve from database
}
//other functions here
//In classes.js
ImageStimulus = (function() {
var $this = function(module_id, unit_id, attributes) {
$this.base.constructor.call(this, module_id, unit_id, attributes);
};
extend(Stimulus, $this, {
initialize: function() {
this.fixation_cross = this.attributes['Fixation Cross'] ? this.attributes['Fixation Cross'] : false;
//do other stuff
}
//other functions here
});
return $this;
})();
var foo = function (module_id) {
var stimulus_objects = [];
var someLimit = 10;
for(var i = 0; i < someLimit; i++) {
//module_id is passed directly to this function
var unit_id = "some source";//some source;
var stimulus_attributes = "some source"; //some source;
stimulus_objects[i] = new ImageStimulus(module_id, unit_id, stimulus_attributes);
stimulus_objects[i].initialize();
}
}
foo(1);

How to properly derive object with private vars using javascript (prototypal) inheritance

I am new to JavaScript's (prototypal) inheritance and I'm trying to learn more about it.
I am using a simple observer pattern as example, in which I want observable objects to be derived from the 'subject' object. This is what I WANT to do:
function subject()
{
var callbacks = {}
this.register = function(name, callback)
{
callbacks[name] = callback;
}
this.unregister = function(name)
{
delete callbacks[name];
}
var trigger = function()
{
var a = arguments;
var t = this;
$.each(callbacks, function(name, callback)
{
callback.apply(t, a);
});
}
}
list.prototype = new subject()
function list()
{
var items = {}
this.add = function(name, item)
{
items[name] = item;
trigger('add', name);
}
this.remove = function(name)
{
delete items[name];
trigger('remove', name);
}
}
Now when using the code above like below, I run into my first problem:
var l = new list()
l.register('observer1', function() { console.log(this, arguments) });
l.add('item1', 'value1'); // <-- ReferenceError: trigger is not defined, trigger('add', name);
To continue testing I made the trigger function 'public' using this.trigger instead. Running my example again I run into the next problem:
var l = new list()
l.register('observer1', function() { console.log(this, arguments) });
l.add('item1', 'value1'); // <-- output: subject, ["add", "item1"]
The this object is subject, I want it to be list. My third problem occurs when creating another list:
var l2 = new list();
//Don;t register any observers
l2.add('item1', 'value1'); // <-- output: subject, ["add", "item1"]
The callbacks list is shared between the 2 lists.
I've tried similar things with Object.create(new subject()) as well and run into similar problems.
My 3 questions in this are:
Can I have private methods that can be used in derived objects (and
should I even care about having them private or public)?
How can I have the this object I want (without needing to use function.call in the derived object, if possible)?
How can I keep the callbacks list in the base object without it being shared?
An interesting question. As for #1 and #2: let's say you have a function foo:
function foo() {
var _private = 'private var!';
this.access = function () {
return _private;
}
}
access is a so-called privileged method, it's a closure that can access the private variable private.
you can inherit the whole thing by making use of call, like so:
function bar() {
foo.call(this);
}
var b = new bar();
console.log(b.output()); // prints 'private var!'
With the methods apply, call and bind you can establish the context of a function, effectively tamper with the this object. (your #2 question, read here )
Naturally you cannot make use of a totally private method in a derived object. You'd need an accessor method which would defeat the purpose of the original method being private. Having said that, that's the way it works in strongly typed languages too (in java if you mark a method as private not even subclases will be able to access it, it would have to be protected).
As for #3, I cannot think of how to keep callbacks shared and private.
But you can make it a static property for all instances of a function (much like a static property in a lanaguage like java) by simply declaring a function like:
function foo() {
}
add your prototypes which will be assigned to each instance
foo.prototype.bar = // ...
and a static property
foo.callbacks = [];
All instances of foo will share the callbacks property.
You can’t have private methods, and that’s that. It will never work both properly and nicely at the same time, so don’t bother trying to emulate them in JavaScript.
Then all you have to do is call the parent’s constructor in the derived constructor.
function subject()
{
var callbacks = {};
this.register = function(name, callback)
{
callbacks[name] = callback;
};
this.unregister = function(name)
{
delete callbacks[name];
};
this.trigger = function()
{
var a = arguments;
var t = this;
$.each(callbacks, function(name, callback)
{
callback.apply(t, a);
});
};
}
list.prototype = Object.create(subject);
list.prototype.constructor = list;
function list()
{
subject.call(this);
var items = {};
this.add = function(name, item)
{
items[name] = item;
this.trigger('add', name);
};
this.remove = function(name)
{
delete items[name];
this.trigger('remove', name);
};
}
Incorporating Joe's suggestion, this is what I eventually ended up with:
function subject()
{
var callbacks = {}
this.register = function(name, callback)
{
callbacks[name] = callback;
}
this.unregister = function(name)
{
delete callbacks[name];
}
trigger = function()
{
var a = arguments;
var t = this;
$.each(callbacks, function(name, callback)
{
callback.apply(t, a);
});
}
}
//without the following line, 'this' in firefox is 'subject' instead of 'list' (in chrome it is)
list.prototype = new subject()
//without these, 'list' is not an instanceof 'subject'
list.constructor = subject;
list.prototype.constructor = list;
function list(n)
{
this.name = n;
subject.call(this); //as suggested by Joe
var items = {}
this.add = function(name, item)
{
items[name] = item;
trigger.call(this, 'add', name); //no way to do this without using call/apply
}
this.remove = function(name)
{
delete items[name];
trigger.call(this, 'remove', name); //no way to do this without using call/apply
}
this.getitems = function() { return items }
}
//without the following line, 'this' in firefox is 'subject' instead of 'queue'
queue.prototype = new subject()
//without these, 'queue' is not an instanceof 'subject'
queue.constructor = subject;
queue.prototype.constructor = queue;
function queue(n)
{
this.name = n;
subject.call(this); //as suggested by Joe
var items = [];
this.enqueue = function(item)
{
items.push(item);
trigger.call(this, 'enqueue', item); //no way to do this without using call/apply
}
this.dequeue = function()
{
var d = items.shift();
trigger.call(this, 'dequeue', d); //no way to do this without using call/apply
return d;
}
this.getitems = function() { return items }
}
var l1 = new list('l1')
l1.register('observer1', function() { console.log('l1', this, arguments) });
l1.add('item1', 'value1');
// ^ 'l1', list { name = 'l1' ... }, ['add', 'item1']
var l2 = new list('l2')
l2.register('observer2', function() { console.log('l2', this, arguments) });
l2.add('item2', 'value2');
// ^ 'l2', list { name = 'l2' ... }, ['add', 'item2']
var q1 = new queue('q1')
q1.register('observer3', function() { console.log('q1', this, arguments) });
q1.enqueue('item3');
// ^ 'q1', queue { name = 'q1' ... }, ['enqueue', 'item3']
console.log(l1 instanceof list, l1 instanceof subject, l1 instanceof queue);
// ^ true, true, false
console.log(q1 instanceof list, q1 instanceof subject, q1 instanceof queue);
// ^ false, true, true
This ticks all of my boxes (except for the use of call, but I can live with that).
Thanks for all the help,
Mattie
EDIT: appearantly this does not work as expected. creating a new object overwrites the other objects callbacks

Why doesn't my public method not appear to exist for my custom object?

I have been trying to work out why my public methods do not appear to exist for my custom jQuery plugin object.
I have created a simplified version of the jQuery plugin with a private variable and two public methods to access/modify the private variable. I have looked online but the results are not that clear, that or my searching skills are terrible.
It keeps saying 'TypeError: myObject.getMyValue is not a function', anyone got a clue as to what I am doing wrong, or suggestions for a better approach to this?
The basic object class is below, but a proper code example can be found on the jsFiddle link.
(function ($) {
var MyClass = function (element) {
var myValue = 'Hello World';
this.getMyValue = function() {
return myValue;
};
this.setMyValue = function(value) {
myValue = value;
};
return this;
};
$.fn.myPlugin = function() {
return this.each(function(key, value){
if ($(this).data('myclass')) {
return $(this).data('myclass');
}
var instance = new MyClass(this);
$(this).data('myclass', instance);
return instance;
});
};
})(jQuery);
var myObject = $('#test').myPlugin();
alert(myObject.getMyValue());
myObject.setMyValue('Goodbye World');
alert(myObject.getMyValue());
http://jsfiddle.net/hZExb/4/
Because you're returning the result of this.each() which would be this. Create a variable outside of this.each() and return that after your this.each has completed.
jsFiddle
$.fn.myPlugin = function() {
var instance;
this.each(function(key, value){
if ($(this).data('myclass')) {
return $(this).data('myclass');
}
instance = new MyClass(this);
$(this).data('myclass', instance);
});
return instance;
};
If you wanted to return an array of MyClass's if your jQuery object was a collection you could do it like this:
jsFiddle
$.fn.myPlugin = function() {
var instances = [];
this.each(function(key, value){
if ($(this).data('myclass')) {
return $(this).data('myclass');
}
var instance = new MyClass(this);
$(this).data('myclass', instance);
instances.push(instance);
});
if (instances.length == 1)
return instances[0];
return instances;
};

How to add data/functions to all instances of a javascript object created by a constructor?

How can I add data/functions to all instances of a javascript object created by a constructor so that all instances have the same reference and not a copy of it?
Basically implementing the equivalent of a static method in C#.
For example, given the following code which creates a Widget class.
(function() {
var Widget = function() {
};
Widget.prototype.init = function(data) {
this.data = data;
};
this.Widget = Widget;
}).call(this);
var instance1 = new Widget();
instance1.init('inst1');
var instance2 = new Widget();
instance2.init('inst2');
alert(instance1.data); // inst1
alert(instance2.data); // inst2
In the above case each instance has it's own copy of the data property. However I want to add a function that sets data for all current and future instances.
My current solution is to add a function to the constructor function object, not to it's prototype. See below for example. Is there any pitfalls to this and is there a better way?
(function() {
var Widget = function() {
};
Widget.prototype.init = function(data) {
this.data = data;
};
Widget.addStaticData = function(data) {
this.staticData = data;
};
Widget.prototype.getStaticData = function() {
return Widget.staticData;
};
this.Widget = Widget;
}).call(this);
var instance1 = new Widget();
instance1.init('inst1');
Widget.addStaticData('static');
var instance2 = new Widget();
instance2.init('inst2');
alert(instance1.data); // inst1
alert(instance2.data); // inst2
alert(instance1.getStaticData()); // static
alert(instance2.getStaticData()); // static
Three pitfalls that I can think of:
methodological: the prototype is the place for shared, reused, inherited functionality/properties - utilise it as such
performance: it is quicker to inherit than to set each time on an instance. John Resig (jQuery creator) did some benchmarking on this in a blog post that I appear unable to find at present.
losing the split between inherited and own properties. If you apply everything to an instance via the constructor, everything is an instance property.
Everything via constructor:
function Dog() { this.legs = 4; }
var fido = new Dog();
fido.name = 'Fido';
for (var i in fido) if (fido.hasOwnProperty(i)) alert(i+' = '+fido[i]);
...alerts both properties as they are deemed the instance's own.
Via prototype and constructor
function Dog2() { }
Dog2.prototype.legs = 4;
var fido = new Dog2();
fido.name = 'Fido';
for (var i in fido) if (fido.hasOwnProperty(i)) alert(i+' = '+fido[i]);
...alerts just name because that is the only instance property. (Nonetheless, fido.legs is retrievable - but it comes from the prototype).
[EDIT - in response to the OP's commet below]
If you want a static method, then that should be added to the function after its declaration.
function Dog() {}
Dog.static = function() {}
Consider a local variable staticData instead of the Widget.staticData property. That way, an external command won't be able to write the data directly, so the only way to write it will be through the addStaticData function:
(function () {
var Widget = function () {};
var staticData;
Widget.addStaticData = function ( obj ) {
staticData = obj.data;
};
Widget.prototype.init = function () {
var data = staticData;
// use data
// or just use the staticData variable directly
};
this.Widget = Widget;
}).call( this );
With your code, one could just execute this:
Widget.staticData = { data: 'COMPROMISED!' };
to change the static data. Since you have a dedicated function for setting the static data, you probably don't want it to be possible to change the static data in other ways.
With my code, the above statement has no effect, and the static data can only be changed via the addStaticData function.

Private Properties in MooTools 1.3+ Classes

I've spent the last couple days researching a way to have private or protected properties in MooTools classes. Various articles (ie, Sean McArthur's Getting Private Variables in a MooTools Class) provide an approach for deprecated versions of MooTools, but I haven't been able to track down a working method for MooTools 1.3+.
Today, after playing with code for hours, I think I have have created a suitable solution. I say "think," because I'm really not that experienced as a programmer. I was hoping the community here could check out my code and tell my if it's actually a valid solution, or a hackjob emulation.
var TestObj = (function() {
var _privateStaticFunction = function() { }
return new Class({
/* closure */
_privates: (function() {
return function(key, val) {
if (typeof(this._data) == 'undefined') this._data = {};
/* if no key specified, return */
if (typeof(key) == 'undefined') return;
/* if no value specified, return _data[key] */
else if (typeof(val) == 'undefined') {
if (typeof(this._data[key]) != 'undefined') return this._data[key];
else return;
}
/* if second argument, set _data[key] = value */
else this._data[key] = val;
}
/* tell mootools to hide function */
})().protect(),
initialize: function() {},
get: function(val) { return this._privates(val); },
set: function(key,val) { this._privates(key,val); }
})
})();
obj1 = new TestObj();
obj2 = new TestObj();
obj1.set('theseShoes','rule');
obj2.set('theseShoes','suck');
obj1.get('theseShoes') // rule
obj2.get('theseShoes') // suck
obj1._privates('theseShoes') // Error: The method "_privates" cannot be called
obj1._privates._data // undefined
obj1._privates.$constructor._data // undefined
I really appreciate any tips! Thanks, everyone!
EDIT: Well, this is embarrassing. I forgot to check out the obvious, obj1._data. I didn't think the this would reference the instance object! So, I suck. Still, any ideas would be awesome!
heh. in your case, a simpler pattern would do the trick.
consider a var behind a closure - extremely hard to puncture. it is available through the getter and setter.
downside: data values cannot be in the instance or they can be accessed directly.
var testObj = (function() {
var data = {__proto__:null}; // 100% private
return new Class({
get: function(key) {
return data[this.uid][key] || null;
},
set: function(key, value) {
data[this.uid][key] = value;
},
remove: function(key) {
delete data[this.uid][key];
},
otherMethod: function() {
alert(this.get("foo"));
},
initialize: function() {
this.uid = String.uniqueID();
data[this.uid] = {};
}
});
})(); // why exec it?
var foo = new testObj();
var bar = new testObj();
foo.set("bar", "banana");
console.log(foo.get("bar")); // banana!
console.log(bar.get("bar")); // undefined.
bar.set("bar", "apple");
console.info(foo.get("bar"), bar.get("bar")); // banana apple
In action: http://jsfiddle.net/dimitar/dCqR7/1/
I am struggling to find a way to puncture this pattern at all - which is sometimes achievable through prototyping like this.
in fact, i played with it some and here's the fixed pattern w/o the namespacing:
http://jsfiddle.net/dimitar/dCqR7/2/
var testObj = (function() {
var data = {__proto__:null}; // 100% private
return new Class({
get: function(key) {
return data[key] || null;
},
set: function(key, value) {
data[key] = value;
},
remove: function(key) {
delete data[key];
},
otherMethod: function() {
alert(this.get("foo"));
}
});
});
var foo = new new testObj();
var bar = new new testObj();
foo.set("bar", "banana");
console.log(foo.get("bar")); // banana!
console.log(bar.get("bar")); // undefined.
bar.set("bar", "apple");
console.info(foo.get("bar"), bar.get("bar")); // banana apple
edit why that is...
my reliance on mootools means my understanding of native js prototypes leaves something to be desired as it abstracts you having to deal with this directly but..
in pattern one, you define AND run the function, which creates the prototype and sets data - a singular instance. you then create new functions with that 'live' prototype where data is already set.
in pattern two, a brand new prototype is created and referenced for each instance, independent of each other. your function returns a new Function with prototype Class... so really new Class({}); hence new new <var>() will create and instantiate the class.
to understand this better, perhaps you can write it like this first - a common enough pattern for creating and instantiating a class that is not being reused - which will make more sense:
new (new Class({
initialize: function() {
alert("hi");
}
}))();
which in turn can be written like this (if saved into a variable):
var foo = new Class({
initialize: function() {
alert("hi");
}
});
new foo();
I hope it makes sense, I am not the best person at explaining...

Categories