Although I think that JavaScript is nearer to C than to Java, I would like to make a juxtaposition of the two languages in this Blog. It won't be so much about what the one can do and the other can't, it'll be more about how to implement a Java class using JavaScript.
Java Iterator
A nice small example would be the implementation of an array Iterator. This is a stateful object that always holds the index of the latest accessed array member.
A stateful object is one that does not look the same all the time, it changes during its lifetime.
For example a JavaList
could change, members may be added or removed, or the order could be changed. You could call itmutable
.
On the other hand a JavaInteger
Object is stateless, it does not ever change, it always stays the number it is. New numbers could evolve from add- or subtract-operations with numbers. You could call thisimmmutable
orfinal
.
The Iterator tells you whether there is another member that has not yet been accessed, and it gives you that next member when so. As soon as you retrieve the next member, its internal index will skip to the next if present. Once the iterator has been consumed it is not usable any more, it must be instantiated (constructed) again for a new iteration.
The iterator abstracts the nature of the iterated collection, which could be a tree, a graph, a list, a table. You do not need to implement any loop counter when using Iterator. Iterator is a design pattern, very recommendable for achieving reusable code with high abstraction level.
So here is a simple Java implementation of iterator:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class Iterator { private final Object [] list; private int index; public Iterator(Object [] list) { this.list = list; } public boolean hasNext() { return index < list.length; } public Object next() { int i = index; index++; return list[i]; } } |
This should be easy to read.
There is a class Iterator that has a constructor that requires an array to iterate (would make no sense without).
It stores the given array into a private final
member field (private
: not visible outside the class,
final
: can not be modified any more from inside the class). The second private
field is the
index that will be used to iterate through the array. The class exposes two public
methods to the outer world,
one returns whether there is another member available, the other returns the next member.
A perfect capsule, binding together data and functions like the OO idea demands.
JavaScript Iterator
To achieve the same in JavaScript we have to know a lot about this language,
especially about closures.
Simply spoken a closure is a block of code that remembers its environment, but this is a little bit too general.
In JavaScript, closures are instantiated function bodies. In Java until version 8, a closure could be realized
for example by implementing a
Runnable,
whereby all environmental variables had to be final
to be accessible by the Runnable body.
In Java 8, closures have been named
lambdas,
and there is a special syntax for them.
But before going too much into details, here is the JavaScript iterator.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | var newIterator = function(list) { var index = 0; var that = {}; that.hasNext = function() { return index < list.length; }; that.next = function() { var i = index; index++; return list[i]; }; return that; }; |
The first line declares a function called "newIterator". I could also have called it "iteratorFactory", but I wanted to make clear that this function resembles a Java class constructor.
This function exists in the global context (use
AMD when you want to avoid such).
For a browser, the global context would be the window
object.
The closure starts with the opening { brace of the function, and with the closing } brace it ends.
All declared parameters of the function, and all its local var
variables,
will be visible and accessible from inside the function,
and from any function or object declared inside this function, down to any depth.
And even when there will be new objects generated from inside and returned to the outer world,
these would all see the same parameter values and local variables,
even when the function gets called several times with different parameter values.
Parameter values would get copied, and local variables would get instantiated newly, on every function call.
So there is an invisible JS mechanism that binds parameters and variables of a certain function call to the code blocks existing inside. This invisible environment is called closure (or lambda) in JS. A function call generates a closure, and that closure copies the current values of parameters, and the closure will survive the function call when it is referenced by some function or object generated inside the function and returned to the outer world.
Mind that a normal { block of code } is NOT a closure in JS! Only functions can provide them.
Using the closure mechanism we can declare the index as local variable within the body
of the newIterator
function. It will be visible and accessible from the returned that
object
and its hasNext()
and next()
functions. The same applies to the list
parameter. Any call of newIterator()
will generate a new closure, binding together
the list
, the index
and the that
object.
Thus we can use factory-functions to imitate both Java classes and constructors at the same time in JS. We can then return an object that uses a closure which holds references to all parameters and variables of the factory function. This might look a little bit unusual for OO developers, but it works fine.
Here they are once more, side by side, for the eye to get used to it, left JavaScript, right Java.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | var newIterator = function(list) { var index = 0; var that = {}; that.hasNext = function() { return index < list.length; }; that.next = function() { var i = index; index++; return list[i]; }; return that; }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class Iterator { private final Object [] list; private int index; public Iterator(Object [] list) { this.list = list; } public boolean hasNext() { return index < list.length; } public Object next() { int i = index; index++; return list[i]; } } |
Keine Kommentare:
Kommentar veröffentlichen