What I am trying to achieve is create some functions that operate on properties of an object in JavaScript, which is a fairly typical thing to do.
The problem is that in the interests of flexibility and extensibility I don't want the functions to know about the object in advance. As far as I can tell this is exactly the kind of thing that Javascript should be pretty good for, but not having spent a lot of time around functional languages I haven't yet got a handle on how to do it.
So I might have a situation like this:
function Owner()
{
var self=this;
self.size = 100;
self.writers = [];
}
function Writers()
{
var Alice= function()
{
var that=this;
that.name="alice";
that.operate = function()
{
console.log( "Alice says: "+self.size );
}
}
this.alice=new Alice();
}
var owner = new Owner();
var writers = new Writers();
owner.writers.push( writers.alice );
owner.writers[0].operate();
Now this returns Alice Says: undefined which is all well and good, but not exactly what I am looking for- I would obviously like to see Alice Says: 100. In a classical language I would probably have to hand the Alice function a reference to the owner ( in pseudo-C# ) :
public class Owner()
{
private List<Writer> writers;
public Owner()
{
this.size=100;
this.writers= List<Writer>();
}
public int Size{ get; set; }
public void AddWriter( Writer writer )
{
writer.Owner = this;
this.writers.Add(writer);
}
}
public class Writer
{
private Owner owner;
private string name;
public void Writer( string name )
{
this.name=name;
}
public void operate()
{
Console.WriteLine( "{0} says: {1}", name, owner.Size );
}
}
As far as I can tell, this is not very Javascripty- it seems like there should be a better- or at least more idiomatic - way of doing it.
In terms of the actual goal of the code I am working with ( which is basically like this, but more complicated ) the goal is to be able to add any number of "Writer" classes that can perform operations on the "Owner" instance to which they are attached. This is basically an implementation of the Decorator pattern and I can think of a lot of ways I could implement this but as I say, I'm looking for understand how the functiony/prototypey nature of Javascript can help me in this kind of situation so I can get a better handle on the language.
I'm also not entirely happy with how I am passing around the functions through instances- I feel like I should be able to throw functions around the place like confetti, so any tips on how doing that interacts with variable scope would be very helpful.
If it turns out what I'm trying to do is ass-backwards and there is a better way of achieving this type of goal, that is fine too. I'm trying to get to the good language in Javascript and it's proving tricky but I'm learning a whole lot.
Edit: My goal is to have a base type where I don't have to know when I design it the complete list of operations it might have to perform and where I don't have to create a new subclass for every different combination of available operations, ensuring my operations are decoupled from my core types.
So if I have an Animal type and then my operations were Eat, Run, Grow and so on, I would ideally have a myAnimal.can("Eat") type call ( I realise that this is a bit like hasOwnProperty but I'm assuming that these operations are of a specific base type ) that would inform me whether that operation was available and then something like myAnimal.actions.Eat("leaves") if it was accessible. When the Eat action was called it would affect the properties of the parent myAnimal object. During the lifetime of myAnimal it might have abilities added or removed at any point, but as long as one checks that it can do something that should be sufficient for my needs.
One way to achieve it is to use javascript closure:
function Owner() {
var $self = {};
$self.size = 150;
$self.writers = [];
function Writers() {
var Alice = function () {
var that = this;
that.name = "alice";
that.operate = function () {
console.log("Alice says: " + $self.size);
console.log($self);
}
}
this.alice = new Alice();
}
var getO = function () {
var writers = new Writers();
$self.writers.push(writers.alice);
$self.writers[0].operate();
$self.writers[0].operate();
}
return getO
}
var o = Owner();
o();
Here is the fiddle.
Having a closure basically creates an environment where, the 'inner-outside' functions and variables are accessible from the 'inner-inside' of a function that has been returned:
These are the 'inner-outside' functions and variables:
var $self = {};
$self.size = 150;
$self.writers = [];
function Writers() {
var Alice = function () {
var that = this;
that.name = "alice";
that.operate = function () {
console.log("Alice says: " + $self.size);
console.log($self);
}
}
this.alice = new Alice();
}
Inside getO is the 'inner-inside':
var getO = function () {
//Here are the functions you want to call:
var writers = new Writers();
$self.writers.push(writers.alice);
$self.writers[0].operate();
$self.writers[0].operate();
}
Returning getO and we've created a closure!
From now on when you call realowner(); you will be liking calling getO(); with everything in getO() being able to access to the 'inner-outside' variables & functions (e.g. $self).
In this line:
console.log( "Alice says: "+self.size );
self is not what you want it to be because your definition of self is not in scope, thus you are getting the global definition of self which is window and won't do what you want.
The issue in your design is that a Writers object doesn't know anything about an Owner object, thus is can't have methods that report the size of the Owner array.
You've made your code much more complex than it needs to be with the needless definitions of that and self and it is unclear why a Writers object should know about it's container. If you want that to be so, then you should pass it's container to the constructor of the Writers object and it can store a reference to it's container.
If you describe better what problem you're really trying to solve, we could probably come up with a massively simpler and more properly object oriented suggestion.
In Javascript, you still need to keep in mind the concept of data encapsulated into objects and those objects having methods that can operate on the data and properties that can be accessed. If you want to operate on the data in an object (as in your .size property), you must have a reference to the object that contains that property.
A reference to an object (like your Owner object) can come from several places:
Writers object..operate(curOwner) method as an argument.What is lacking in your code is any way from the .operate() method of the Writers object to get to the corresponding Owner object. It simply isn't available from within the .operate() code. If you want it available there, you will have to change the structure of your code to make at least one of the above access methods or invent a new way to get to the Owner instance.
Based on your latest edit to describe the problem, here's some more info. One of the ways you can take advantage of javascript's loose typing is that you can have an array of objects and those objects can be of any type you want. As long as they all have a common set of methods such as .eat(), .grow(), .run(), you can treat the objects all the same (calling these methods on them) even though they are completely different types of objects. You don't have to make a common base class and then subclass from that. That's not very javascripty and not required. Just make three different objects, give each the required set of methods, put them in the array and then call the methods as needed. You can even test if an object has a particular method before calling it if they might not all have the same methods (though this is a less perfect OO design - though sometimes more practical than other designs).
For example, you can have this:
// define the cat object
function cat(name) {
this.name = name;
}
cat.prototype = {
eat: function(howMuch) {
// implementation of eat here
},
run: function(howFar) {
// implementation of run here
},
grow: function(years) {
// implementation of grow here
}
};
// define the dog object
function dog(name) {
this.name = name;
}
dog.prototype = {
eat: function(howMuch) {
// implementation of eat here
},
run: function(howFar) {
// implementation of run here
},
grow: function(years) {
// implementation of grow here
}
};
You can then have an array of cats and dogs:
var animals = [];
animals.push(new cat("fluffy"));
animals.push(new dog("bowser"));
animals.push(new cat("purr"));
You can then have code that operates on the contents of the animals array without regard for what kind of animal it is. So, to make each animal run 30 feet, you might do this:
for (var i = 0; i < animals.length; i++) {
animals[i].run(30);
}
Now, you could make a base class that has a default constructor to save the name and defines do nothing abstract methods for eat, run and grow (in C++ or C# style), but that often isn't really necessary in JS because you don't have to know anything about the type of the object to use it as long as they all have some common set of methods you know how to call.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With