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
First of all, I took the animals example from the coffeescript site.
I want to simulate next things in javascript:
Classes
Public methods only
Private methods and variables only
Inheritance
Call methods from the super class
I think this way to create this is ok, but when I try to get the move method from the parent class, always it returns to itself. What I'm doing wrong?
BTW. Which are the best practices to achieve my goal? Is right what I'm doing?
var Animal = (function() {
function Animal() {}
var _private = {};
var _public = {
move: function() {
console.log('Can move');
}
};
Animal.prototype = _public;
Animal.prototype.constructor = Animal;
return Animal;
})();
var Snake = (function(_super) {
function Snake() {}
var _private = {};
var _public = {
move: function() {
console.log(Snake._super_.move);
console.log('Slithering');
}
};
Snake.prototype = _super.prototype;
Snake._super_ = _super.prototype;
for(var method in _public) {
if(Object.prototype.toString.call(_public[method]) === '[object Function]') {
Snake.prototype[method] = _public[method];
}
}
return Snake;
})(Animal);
var s = new Snake;
s.move();
This is very well written code in my opinion, with just one small mistake.
I think you got your pointers a little crossed, try this:
<script>
var Animal = (function () {
function Animal() { }
var _private = {};
var _public = {
move: function () {
console.log('Can move');
//this just returns a string to show which method was called
//inside of the child's move function's console.log
return "super move called";
}
};
Animal.prototype = _public;
Animal.prototype.constructor = Animal;
return Animal;
})();
var Snake = (function (_super) {
function Snake() { }
var _private = {};
var _public = {
move: function () {
console.log(Snake._super_.move());//Now we can call super's move
console.log('Slithering');
}
};
//This created the circular reference where Snake._super_ was pointing to
//Snake.prototype which was causing the error
//Snake.prototype = _super.prototype;
Snake._super_ = _super.prototype;
for (var method in _public) {
if (Object.prototype.toString.call(_public[method]) === '[object Function]') {
Snake.prototype[method] = _public[method];
}
}
return Snake;
})(Animal);
var s = new Snake;
s.move();//now this outputs "Can move", "super move called", "Slithering"
</script>
If you are asking for best practices, I'd say take any of ready to go solutions on the web. I prefer this one: http://canjs.us/#can_construct.
A few notices about your approach:
It's not reusable. You have to write the same code for every single class. At least you should extract for-loop to make this piece of code reusable.
You need to check _public.hasOwnProperty(method) to make your code more robust.
toString and valueOf methods require special handling since they are non-enumerable in IE<9.
Snake.prototype = _super.prototype; is a complete disaster. Since your super class will have all methods of child.
var F = function(){};
F.prototype = _super.prototype;
Snake.prototype = new F();
Snake.prototype.constructor = Snake;
This is how I create objects from a hash of properties:
var object = new function (data) {
var self = this;
self.property = data.property;
self.anotherProperty = data.anotherProperty;
self.method = function () { return 'something'; }
self.update = function (newData) {
//what is here ?
//i could have written:
self.property = newData.property;
self.anotherProperty = newData.anotherProperty;
//but why not reuse the constructor?
}
};
I wonder how to reuse this function (constructor) to update an object from hash.
So that:
object.update(newData)
would update current object properties from newData hash the same way it is done in the constructor function.
By giving the constructor a name?
function MyNotReallyClass(data){
var self = this;
self.property = data.property;
self.method = function () { return 'something'; }
self.update = MyMyNotReallyClass;
};
you can can now call
var obj = new MyNotReallyClass(data);
var obj2 = new MyNotReallyClass(data);
and
obj.update(data);
i hope this helps.. i not 100% sure, because i'm learning too.. but yeah try it ;)
edit: after reading this comment of you: "But that would return a new instance, wouldn't it? Which i don't want."
i think you can write the Update function and call it in your constructor
var object = new function (data) {
var self = this;
self.update = function (newData) {
self.property = data.property;
self.method = function () { return 'something'; }
// and other things You want to do in constructor and update
}
self.update(data);
}
;
If I have a javascript class that runs some initialization code, it seems logical to put this code at the top, and any methods at the bottom of the class. The problem is, if the initialization code contains a method call, I get a 'undefined' is not a function error. I imagine because the method is defined after the method call. How do people normally structure javascript classes to avoid this? Do they put all the initialization code at the end of the class? For example:
var class = function() {
this.start();
this.start = function() {
alert('foo');
};
};
var object = new class();
causes an error, while:
var class = function() {
this.start = function() {
alert('foo');
};
this.start();
};
var object = new class();
does not. what would be considered a good structure for a javascript object like this?
Here's what I would do
// create a "namespace"
var com = com || {};
com.domain = com.domain || {};
// add "class" defintion
com.domain.MyClass = function(){
var privateFields = {};
var publicFields = {};
privateFields.myFunction = function(){
// do something
}
publicFields.initialize = function(){
privateFields.myFunction();
}
return publicFields;
}
var myClass = new com.domain.MyClass();
myClass.initialize();
of course, you could just make initialize(); "private" and run it before return publicFields;
var class = function() {
this.start();
};
class.prototype.start = function() {
alert('foo');
};
var object = new class();
now you can mess around with start as much as you want in the constructor. but dont use the keyword class as it is a reserved word.
I like http://ejohn.org/blog/simple-javascript-inheritance/ where I feel much more programing OOP.
var myClass = Class.extend({
init: function () {
this.foo();
},
foo: function () {
alert("foo");
}
});
new myClass();
Consider making start a method on class's prototype. This has the bonus of saving memory since all instances of class can share the same start, instead of creating a new start function for each instance.
var class = function() {
this.start();
};
class.prototype.start = function() {
alert('foo');
};
var object = new class();
I actually work a lot with structures like this:
var foo = function() {
// Actual construction code...
start(); // <-- doesn't make a difference if functions are
privateMethod(); // public or private!
// ---------------------------------
function start() {
// ... whatever ...
};
function privateMethod() {
// ... whatever ...
};
// ---------------------------------
// Add all public methods to the object (if start() was only used internally
// just don't assign it to the object and it's private)
this.start = start;
};
Given the following:
var someObject = {};
someObject.prototype.a = function() {
};
someObject.prototype.b = function() {
//How can I call someObject.a in this function?
};
How can I call someObject.a from someObject.b? Thanks.
This will work:
someObject.prototype.b = function() {
this.a();
};
However your definition of someObject is slightly wrong, it should be:
var someObject = function() {};
Test script:
var someObject = function() {};
someObject.prototype.a = function() {
alert("Called a()");
};
someObject.prototype.b = function() {
this.a();
};
var obj = new someObject();
obj.b();
I think you probably meant to do this:
function Thingy() {
}
Thingy.prototype.a = function() {
};
Thingy.prototype.b = function() {
this.a();
};
var someObject = new Thingy();
It's constructor functions, not plain objects, that have a special prototype property. The prototype of a constructor function is assigned to all objects created with that constructor via the new keyword as their underlying prototype, which gives them default properties (which may reference functions, as they do above).