-
Notifications
You must be signed in to change notification settings - Fork 1
Guide
This guide serves to briefly explain the design of the Jay library and how it enables a slight reconceptualization of the Javascript language in order to uncover a more intuitive, consistent and elegant language that sits behind the often misunderstood aspects of the language (Note: the guide is still in editing, comments welcome).
Javascript is an elegantly small and flexible language and has become one of the fastest scripting languages available. It is deceivingly ubiquitous, running on all web browsers, and recently a solid option for server programming, it is increasingly becoming a more crucial aspect of web development.
Javascript is also a language that is rumored to have been developed in just ten days in a rush to get to market.
As a result, the language admittedly lacks features and contains many as yet unaddressed flaws that make it difficult to develop in, harder to learn, and much less powerful than it can be.
Due to the intense flexibility and nature of javascript, it is possible to adapt the language to make it more intuitive and powerful. This is the aim of the Jay library, and the Jay Project.
The Jay Project aims to empower and guide javascript development, making javascript a more friendly (and powerful) language.
This involves researching difficulties of the language, instructing on how to overcome the issues, teaching best practices, and most importantly, providing utilities that empower javascript development.
By looking closely at the nature of Javascript, and designing the usage of the language through new utilities, the Jay project has proved that it's possible to reconceptualize the Javascript language, as a language that is elegant, more true to it's own nature, easier to understand, simpler to use and more powerful.
The Jay library provides core language utilities
that extensively enhances the core of the Javascript language,
creating simplicity by abstracting away inconsistencies in the language,
providing a more powerful set of mechanisms,
solving commonly encountered problems,
and providing a clear common framework that automatically leads developers
to the proper usage of the Javascript language.
The library provides a framework for the following aspects of javascript:
- Flowing with the nature of javascript
- Manipulating Objects
- Creating Types
- Type Checking
- Iteration
- Namespacing Packages
- Concurrency
Knowledge of the Javascript syntax is outside of the scope of this guide. You are expected to have a basic understanding of how to use Javascript, but this section gives you some example code to make sure you understand these things, and act as a slight reference.
var user; // undefined
var anotherUser = null; // null (not undefined)
name = "Tim";
age = 25;
var hello = function(name, anotherSentence) {
console.log('@' + name + ': hello. ' + anotherSentence)
};
var hello = function(name, anotherSentence) {
var talk = function(msg) {
console.log('@' + name + ': ' + msg)
};
talk('hello')
talk(anotherSentence)
};
Each programming language has unique features, and the combination of those features itself is unique.
There are several things I'd like to highlight up front that we should appreciate about javascript's nature when deciding how we structure our programs.
In javascript we can very easily create a plain object and customize it toward what we want to use it for. In other languages you would usually need to make a special type and then instantiate it. Often you will still want to use typing in javascript, and it is within it's nature to do this.
Using customized objects is most useful when we want to keep a nice readable syntax, as we are invoking a function. It is also extremely useful when defining types themselves.
The process of customizing objects is a core aspect to the language.
Javascript is a what is known as a 'functional language'. Functions are first-class citizens of the language, every bit as maleable and usable as other types. As a result they can be used to achieve great flexibility.
Passing a function in as a parameter is possible with Javascript, but what's more important is that the function can be inlined within it's parameter place. This is extremely useful when continuing the definition of execution rules, and inlining functions in parameters can greatly contribute to readability, and should be considered a core usage of the language. Therefore, when designing functions which can have other functions passed to them, the choice of having other parameters trailing after potentially inlined function parameters is highly discouraged.
One of the most powerful features in Javascript is the ability for nested functions to act as closures. The variables in the scope of parent functions is always available to the nested functions. When inlining functions this is always something that should be kept in mind.
As mentioned, the act of creating customized objects is core to
- and follows from - the dynamic nature of the Javascript language.
This section will show you how the core mechanisms of the Jay library are used to define objects.
There are three means of defining objects:
- Constructing an instance of a type
- Customizing an existing object
- Creating an object which inherits from any other Object
The first approach - "construction" - is achieved through Javascript's new
operator which runs a constructor function.
The second approach - "customization" - is not catered for by Javascript itself, and therefore Jay provides utilities and syntax for this case. The usage of customizing objects runs throughout the library and can be seen in Jay's type utilities and namespacing utilities.
The third approach - arbitrary inheritance - was also not originally catered for by Javascript, however, the most recent version of Javascript (ECMAScript v 5) does add support for this approach by default, but this version of Javascript is not yet supported by all browsers / engines. Jay makes sure that this functionality is provided across all browsers, as it is a mechanism that can be implemented outside of the language engine itself.
The difference between constructing an instance of a type, and creating an object which inherits from any other Object is that for the latter, you don't need to run a constructor to achieve the inheritance. This also means you don't need to have a type defined in order to achieve inheritance.
A noteworthy fact is that the proper creation of custom type hierarchies relies severely on this ability to create an object that inherits from an arbitrary object, despite the fact that it has only been included in the language recently. And the practice of not using this mechanism when defining type hierarchies is extremely discouraged.
A practice that is fundamental to the usage of Javascript is overlaying one object's properties on top of another object.
This is usually used as a means to customize many properties of the object in one step, or to make sure that an object contains the same values as some other object.
Jay provides overlay()
and underlay()
as core functions
that are fundamental to this practice. They can be considered "mixin" functions,
and mixin()
is in fact an alias to overlay()
.
The overlay
function provides assurance that an object will hold the same values
attached to it's properties as some other object holds at those properties.
If the process where visualized, the object being customized would have the other object placed as a layer on top of it, and the result of that customized object would then be like you are looking at it through the overlayed object.
The overlay function will overwrite any properties that need to be set, even if they exist already. This is why we need a different function, which does not overwrite existing properties.
The underlay
function provides assurance that an object has the
same properties set as some other object has set.
Unlike overlay
, it does not ensure that the values at the properties
are the same as the other object's.
This is frequently used when we want to ensure that an object looks similar to another object. We usually are assuming that if the property already exists on the object, then it holds a customized version of the same value which is held by the underlaid object.
The afformentioned mixin functions are two of the core mechanisms of customization. They provide utility that should be considered core functionality of the language, however, they are not specific mechanisms, as opposed to a mechanism that allows us to customize an object how ever we see fit.
Jay provides a general purpose mechanism that encapsulates the practice of customization, and allows us to take an object and run our own block of code on it.
Having this utility - as opposed to just running the code directly - increases readability of code (the purpose of the code is clearly indicated), allows us a private scope for the object, and encapsulates a practice that is fundamental to knowing how we should use javascript. In many cases, such as when defining types, this utility plays a fundamental role.
The customize
function provides this utility.
It takes an existing object and a definition block
which will be executed with the 'this' variable set to the object.
var tim = customize({}, function() {
var time = new Date();
this.name = "Tim";
if (time.getHour() > 22 && time.getHour() < 6)
this.isSleeping = true;
else
this.isSleeping = false;
})
To create more consistency, you can also use the customize function when you just want to overlay something. The act of overwriting any existing property is customizing that existing object, so it makes sense that the function does overlaying and not underlaying.
var tim = customize({}, {
name: "Tim",
isSleeping: false
})
Arbitrary inheritance is the third means of object manipulation. As opposed to constructing an instance of a type, or customizing an existing object's properties, it is possible to create an new object that will have prototypal inheritance from an existing object.
This can be used instead of the mixin functions in some cases, but the difference is of course that the parent object is being accessed and potentially being manipulated when the child object is being accessed.
Jay ensures that the ECMAScript function Object.create is available, and also provides a global version of this function, because it is a fundamental means of object manipulation.
var timTheRunner = customize(create(tim), {
speed: 10
})
As mentioned, arbitrary inheritance plays a crucial role when defining prototypes.
Element.prototype = create(Node.prototype)
As a result of Javascript's prototypal nature, it is frequently the case that a new object is created which inherits from another, and then that object is customized. This can be considered "specialization".
Jay provides a function called specialize
which combines the arbitrary inheritance of the create
function,
with the customize
. This function is especially useful for the definition of types.
Element.prototype = specialize(Node.prototype, function() {
this.getElementsByTagName = function() {
};
});
The Javascript type system is highly transparent and more maleable than might be expected, as is most of javascript.
Javascript's type system is purely based on prototypes, although it might sometimes seem that constructors are the main player, that isn't the case at all, they are just the front faces.
Javascript's type system has often been considered a complicated obstacle to overcome when learning the language. It can easily be marked as the area of which the language is most confused about itself, resulting in a lack of strong direction, inconsistency and errors in the language design.
The greatest contribution that the Jay library makes to the language is in it's uncovering of the truely elegant nature of the type system that sits within the Javascript language.
Through redesigning the type system into what would be more desirable, while sticking as closely as possible to the existing type system and the core nature of the language as a whole, the Jay library has been able to make sense of the existing type system and reshape it into what it perhaps should have looked like from the beginning.
This section will present a view of the type system that flows with the nature of the language, is consistent, simple and powerful.
In Javascript, you can create an object and customize it as you like, but that doesn't make it a member of a different type.
Constructors are plain functions which serve to construct an object (customize it) as necessary for the type of object desired. You can create a constructor function to create objects that represent users for example.
When the new
operator is used with a constructor function,
a new object is created and passed to that constructor function to customize.
The new operator and the prototypal inheritance it provides, is what inserts the magic into the type system, it is what sets up the prototypal inheritance chain that distinguishes different types.
If the new operator wasn't used, and all the constructor function did was some customization of the object, it wouldn't really be creating a different type of object, it would just be some random customization. This is how the prototypes and the new operator come into the picture and create a type system.
Each function has a corresponding prototype object. When ever the new operator is used on that function, the new object that is created will inherit from the prototype object. Every object that is created by that constructor function will obviously inherit from the same prototype, hence the name 'prototype'.
The prototype object is then able to hold functions on it that are common to all instances of that type. Creating types is therefore done through the action of prototyping.
Because an object inherits from the prototype, it's possible to determine what type the object is by checking whether it inherits from a certain prototype. The distinction of types is enabled by this inheritance, and this is how Javascript type checking utilities are implemented.
This is what empowers the Javascript type system, prototypes. Constructors are effectively used as the front faces of types.
By having constructors as the front faces, we get a very direct feeling of what is happening, whenever we create a new instance of a type, we want some customization for that instance of the type to be executed on the new instance, this is the purpose the constructor function serves.
Prototypes allow us to distinguish between types of objects, for example, animals vs vehicles. It is also possible to create type hierarchies, where one type is a sub type of another. All instances of the sub type are then also considered instances of the parent type.
This is achieved by creating prototype objects that inherit from the parent prototype. It is important that this inheritance chain not be created through invoking the parent constructor, because constructors must only be run for instances, the sub prototype is not an instance itself, an instance is only created when the subtype's constructor is run. We don't want the parent constructor to execute it's logic on the sub prototype object, because that logic only applies to actual instances of the type, so we bypass the constructor when setting up type hierarchies, and run the parent constructor only when the sub constructor runs (by invoking it from there).
This is why the arbitrary inheritance mechanism is so crucial to setting up type hierarchies.
The notion of a prototype based language is a well known approach to programming languages, and was not invented by Javascript, it was chosen.
Javascript is a language that aims to be very transparent and maleable. Core mechanisms of the language are often exposed directly to us, and are not surrounded by an aura of magical abilities. It is possible to rewrite many core language mechanisms in Javascript itself, in fact that is what empowers the Jay library to achieve what it does.
The prototypal approach to typing satisfies this aim by making the core aspect of the typing system - prototypes - just another object the same as all the rest, and a constructor function that is used with the new operator is just another function that can be used like any other function. The association between the constructor and the prototype is also just a simple property assocation, and something that is crucial we use when setting up type heirarchies as discussed later. Having types represented by their constructors (that will be run on instantiation) adds further to the transparency and directness of the language.
Prototypes also have several other benefits to their actual approach of typing.
Creating many instances of a type becomes a very inexpensive operation. Because the functions for a type are defined on the prototype and inherited by instances, the actual instance objects can be much more lightweight, usually holding no functions on itself.
With prototypal inheritance we have the frequently used ability to manipulate all instances in one go by manipulating just the prototype. The link between the instances and prototype is live, so changes to the prototype are seen on all instances. We can therefore change the functionality of all instances by replacing one of the prototype's functions.
There are also other abilities, such as setting default values and creating different versions of an object.
A type can be defined simply by creating a function which will act as a constructor. All functions have the ability to act as constructor functions, as Javascript creates and associates a new prototype object for each function. To some degree this is actually an error in the language, and the language should have differentiated normal functions from constructor functions.
The prototype that the constructor represents is always available through a property on the constructor function, by the name of 'prototype'. This can be considered a pointer to the prototype that will be used for all instances. By manipulating this property we can change the prototype that is used for any new instances created with the constructor, or we can access the prototype object to manipulate the properties that all instances of the type will inherit.
var Node = function(textContent) {
this.textContent = textContent
}
Node.prototype.toString = function() {
return this.textContent
}
var n = new Node("Hello")
When a constructor function is created, Javascript creates a new prototype object.
We can change the hierarchy of a type by using a prototype object that inherits from some other prototype object.
By default the prototype object created by Javascript for all functions is one that inherits directly from Object.prototype.
If we wish to create the hierarchy differently we need to tell javascript to create a different prototype object to use with the constructor.
Unfortunately, Javascript has never had a mechanism which exposes this ability to change the hierarchy of a type. It is something that previously had to be done manually or provided by some add-on framework. This is something that Javascript should really have provided from the beginning.
The process of manually specifying a new prototype is often exactly where the complexity of the Javacript type system hits it's peak.
Jay provides the constructor
utility which encapsulates the definition of a constructor function, adding the ability to specify the parent type of a constructor.
By doing this Jay makes up for some very important functionality that Javascript has not provided for the type system, the ability to create type hierarchies.
Whenever you want to create a constructor function, you can use the constructor
utility
to do so.
This utility creates and associates a prototype object of the desired descent with the given function which is to be used as the type constructor.
You simply pass in the function that will act as a constructor, as well as the parent type.
The constructor
utility will create and associate with the function a new prototype object of the desired descent.
Remember that Javascript type's are represented by constructor functions, so when specifying the parent type we pass in the parent constructor function, for example:
var Element = constructor(Node, function(textContent, tagName) {
Node.call(this, textContent)
this.tagName = tagName
})
This enhances the readability of the code tremendously, and makes the creation of constructor functions much more conceptually coherent.
If we wanted to change the hierarchy of an existing type,
we can still use the constructor
utility to redefine the prototype as such:
constructor(OtherNode, Element)
Not having to access the prototype property is what takes much of the messyness away. The fact of the matter however, is that creating a constructor function is not equivalent to defining a type. We still need to access the prototype to add methods to the type. Javascript does not have any mechanisms which encapsulate the definition of a prototype.
As a result, we incorrectly conceive types to be defined through the creation of constructors, and therefore understand constructor functions to be the most important element of the type system. This understanding is what can make the Javascript type system complicated and difficult to understand, as it is not the true nature that has been hiding in the javascript type system.
Defining a type in Javascript is done through the action of prototyping. By understanding the Javascript type system as a language that is wholely centered around prototypes and the action of prototyping we can significantly simplify the language and see that it is coherent, understandable and elegant.
Javascript is a purely prototypal language, with types represented by constructors. Defining a constructor function is just a shortcut to prototyping.
The full force of the Javacript type system can be understood and simplified through the process of prototyping.
The language does not come with any utilities to directly create prototypes, and no frameworks that I've come across have ever decided to present such a utility.
The Jay library identifies prototyping as the core and more general mechanism in the type system, and provides an intrinsic utility for prototyping which runs completely true and consistent with the nature of the type system and Javascript as a whole.
Javascript is a purely prototypal language, where a type is represented by the constructor function on the prototype.
In Javascript, every prototype object has a property that holds it's constructor function. The constructor function holds the initialization logic that is executed when an instance of the type is created. This property of the prototype is called 'constructor', and also serves to indicate on each instance object, the exact type of the instance.
The entire type system in Javascript is actually defined by the prototyping of the instances of that type, even the constructor function itself is just a part of that prototyping.
Jay provides a type
mechanism (alias prototype
)
which serves to prototype the instances of a type.
Prototyping runs true to the same nature seen with object manipulation, it is a specific case of object manipulation, in which we are specializing the parent prototype object.
To define a type, you specify a parent type and provide a definition block for the prototype.
The type's constructor is returned because in Javascript, types are represented by constructors.
var Element = prototype(Node, function() {
this.constructor = function() {
}
this.toString = function() {
return '<' + this.tagName + '>';
}
})
The prototype
utility is a more general mechanism than constructor
.
Infact, constructor is simply a shortcut, and forwards to prototype
as follows:
function constructor (parent, C) {
return prototype(parent, { constructor: C })
}
This neatens up the creation of types tremendously, and simplifies the understanding of the Javascript type system. All nuances of the type system make perfect sense through this conceptualization, and the nature of the language shines through clearly.
Conceptualizing Javascript in this way brings prototyping back to the forefront of the language, with ease and simplicity. This approach also allows prototyping to bear remarkable resemblance to the definition of types in other programming languages.
Javascript is a purely prototypal language, with types represented by their constructors.
By understanding the Javascript type system as a language that is wholely centered around prototypes and the action of prototyping, we can see clearly into the true nature of the language and see that Javascript is consistent, coherent and elegant.
Type checking involves determining what exact type a value is and whether it is on a certain type hierarchy and therefore conforms to the behavior that may be required of it.
We frequently need to route logic according to a value type, check whether a parameter has not been specified, ensure that a value is of a certain type, or determine the type so that we can create a new object of the same type.
The type checking mechanisms in Javascript are powered by determining whether a value inherits from some type's prototype.
Javascripts built in type checking mechanisms are unfortunately a very weak aspect of the language. They are not consistent in how they conceptualize the types and how they report on them, nor are these mechanism designed according to our usage.
The Jay library provides type checking mechanisms that are more powerful and designed with our use cases in mind, therefore simplifing the conceptualization of the types and what we need in order to reflect on them.
Javascript has several types built into the core language. Two of these are special types that are considered 'void' types. All the non-void types inherit from the root type Object.
Three of the built-in types have primitive equivalents which will be discussed below.
- undefined
- null
- Object
-
Boolean
-
Number
-
String
-
Function
-
Array
-
Date
-
RegExp
-
Error
-
Every language has primitive types, which offer greater performance at the cost of reduced functionality.
Javascript provides three primitive types, Boolean, Number and String. These types can be created as either functionality-deprived primitive versions, or as functionality-rich object versions.
The two void types - null and undefined - are considered primitive as well.
A primitive type, unlike a non-primitive, cannot have any properties set on the it, it can however still have properties read off of it. The primitive types inherit from the type's prototype just the same as the non-primitive versions of the types do, so it is possible to add functionality to primitive types by manipulating their prototype object.
Because the primitive types cannot have properties set on them, they are not considered an 'instance' of the type, and can instead just be called a 'value'. Sometimes they are also not considered an 'object' either, but they do allow object reading.
Jay provides the isPrimitive
function that can be
used to check whether a value is primitive.
When it is we should not attempt to set any properties on the object.
Jay also provides three functions - isVoid
, isNull
, isUndefined
-
to check whether a value is one of the two void types.
Null and undefined are two distinct types, and void is a pseudo-type,
a void value is either null or it is undefined.
These functions are extremely useful for checking
whether a parameter has been specified or a variable has a value.
Primitive values are created through the special syntax for each of the types,
and non-primitive values are created by using the new operator with the type constructor.
The undefined value is used whenever a value has not been set
(even after the variable is declared), and null
is an actual variable that
references the null value.
var str = "hello"; // isPrimitive(str) === true
var num = 1234; // isPrimitive(num) === true
var bool = false; // isPrimitive(bool) === true
var u; // isPrimitive(u) === true; isUndefined(u) === true
var n = null; // isPrimitive(n) === true; isNull(n) === true
var obj = {}; // isPrimitive(obj) !== true
var f = function() {}; // isPrimitive(f) !== true
When we want to know exactly what type a value is,
regardless of whether it is a primitive or non-primitive version,
we can use Jay's typeOf
function to determine the exact type.
Jay's typeOf
utility accepts one value and returns the constructor
of the most direct type that the value inherits from.
This is the constructor of the actual value,
and for non-void types is available on the constructor
property of the value.
This utility also accounts for an important aspect of type checking regarding cross engine interaction. If a value is created by another Javascript engine, which is the case when it is created in a separate browser window or worker thread, then the constructor function is actually a different function to the same one in the original engine your code is executing on, effectively making it a completely different type even though it's the same type just operating within another engine / frame. This utility takes this issue into account and successfully translates all of the built-in types to the equivalents within the engine your code is running on. It is not possible to do this for user-defined types, as such it is recommended that cross engine interaction be done only with builtin types or serialization.
If you want to create a new value with the same type,
then you can use the constructor returned from typeOf
to do so,
except of course if it is one of the special void types
- null or undefined - which cannot be constructed.
Note that the new value will obviously not be primitive if you
use the
new
operator such asnew typeOf(myvar)
, even if the original value was primitive.
The void types are indicated by returning the value itself, either undefined or null, and can therefore be checked for as expected:
if (!isUndefined(typeOf(myvar)) && !isNull(typeOf(myvar)))
var othervar = new typeOf(myvar);
User-defined types are indicated exactly as built-in types are, which makes this a consistent and powerful mechanism.
This utility only indicates the direct type of a value, it does not indicate whether a value is indirectly a given type, as a result of being of a type that inherits from the given type.
The most frequent and powerful usage of type checking is to determine if a given value is indirectly (or directly) of a given type.
Jay provides the istypeof
function to determine whether the value
- regardless of being primitive or not - is indirectly (or directly) of a given type.
It achieves this by looking at whether the object inherits from the given type's prototype.
Jay also comes with a similar function isinstanceof
which determines
the same as istypeof
except that it excludes primitives,
so only reports true if an value is not primitive and is of a certain type.
A similar function is available for testing the opposite case,
if a value is of a primitive version of a certain type.
To check for this you can use isprimitiveof
,
this is just a shortcut to istypeof && isPrimitive.
The two main functions of type checking
are istypeof
and isPrimitive
,
using these utilities is the preferred method,
as it is more powerful and the logic is more straight forward than when using isinstanceof
or isprimitiveof
.
istypeof(new String, String); // true
istypeof("hello", String); // true
istypeof("hello", Object); // true
istypeof("hello", Number); // false
isprimitiveof("hello", String); // true
isprimitiveof(new String, String); // false
isinstanceof("hello", String); // false
isinstanceof(new String, String); // true
The type hierarchy reflection functionality is powered by the ability to check the inheritance chain of any object. Just like Javascript didn't originally provide a mechanism for arbitrary object inheritance, it still doesn't provide a mechanism for testing arbitrary inheritance.
Jay provides a function isheirof
which does testing of arbitrary object inheritance,
unlike istypeof
which can only test against a type (constructor).
isheirof(new String, String.prototype); // true
isheirof("hello", String.prototype); // true
isheirof("hello", Object.prototype); // true
isheirof(5, Object.prototype); // true
isheirof(5, String.prototype); // false
All Javscript objects can be iterated. It's important to know that there are two very distinct forms of iteration.
One form is collection iteration, where the elements of a collection - such as an Array or a NodeList - are iterated through.
The other form is property enumeration, where the properties of any object - including Arrays - are enumerated.
Javascript provides two main constructs that are used for iteration, these two constructs reflect the two different types of iteration. There is also a third form which is not provided by Javascript, but is actually an approach user's take to specify a custom iteration process for an object type, regardless of what form of iteration is being done.
The for
loop construct is used to iterate through
numerical-based collections such as Arrays,
using a traditional counter based approach. At a minimum,
a collection object would have a 'length' property which the
counter is compared against, in conjunction it would have some
means to access an item at the given numerical position.
A NodeList exposes an item()
function which can be used to
obtain an item at some position. An Array on the other hand,
must be indexed by using property access.
This means that there are at least two different ways to code a
collection iteration and you'll need to know what type of object you are iterating, this is an issue that the Jay library overcomes.
for (var i = 0; i < array.length; i++)
console.log(i + ' = ' + array[i])
for (var i = 0; i < object.length; i++)
console.log(i + ' = ' + object.item(i))
The second loop construct is the for in
loop,
and is used exclusively for the second type of iteration,
property enumeration.
All objects - even Arrays and Strings - can have their properties enumerated through.
Property enumeration should never be used to iterate through the elements of collections such as Arrays or Strings (a string is collection of characters). This warning is given because these objects expose their elements as properties as well, often making user's think that property enumeration can be used to iterate through the elements, meanwhile it is really enumerating through all other properties on the collection as well.
for (var prop in object)
console.log(prop + ' = ' + object[prop])
The third construct for iteration is not something provided
by Javascript but is rather a practice that is frequently
done by user's, in order to specify some custom iteration logic. For any object it is possible to place a
method on the object, usually under the each
property,
which implements a user-defined iteration approach
specific to that type of object. The each
function
will accept the callers iteration function which it will
execute on each element of the iteration.
Jay's iteration system utilizes utilizes and empowers this approach for achieving custom iteration, and also provides a richer system for iteration which allows one type of object to have different types of iteration.
The Jay library provides an iterator-based system that overcomes many of the difficulties of Javascript iteration, and provides a much more power to iterations.
Iterator objects exists to encapsulate a specific type of iteration on an object. By keeping a separate iterator object it is possible to use one common loop construct which will automatically select an appropriate iterator for a given object.
Jay provides the iterator
function which will automatically
get an iterator for a given object.
Jay also provides a foreach
loop construct which can be
used to loop over any given object in the same way,
regardless of it's type. The foreach
function will
get an iterator from iterator
and run the iteration.
foreach(array, function(element, i) {
console.log(i + ' = ' + element)
})
You can also pass an iterator directly to foreach
yourself, which allows you to specify what type of iteration to execute, for example, maybe there is a reverse order iterator, or a depth-first tree iterator for the object.
foreach(new MyTreeIterator(treeObj), function(node, i) {
console.log(i + ' = ' + node)
})
Iterators and the foreach
function also provide some advanced
features that Javascript's built-in iteration constructs
don't provide.
Unlike the built-in constructs, the foreach
construct
can allow the user iteration function to return a value
as the result of the iteration. This can be used to
very good effect as will be demonstrated later.
The iterator system also provides the ability for the user iteration function to break out of the iteration at any point, as well as the ability to manipulate the result of iteration in any way desirable. This gives the user iteration code a common interface with which to execute this functionality.
Before the iterator
function chooses an iterator for an object, it will first check whether the object specifies
it's own custom iteration.
An object can customize it's iteration in two ways,
by specifying it's own iterator (this option has the highest priority), or by the preferred and simpler method of specifying an each
property that implements the iteration.
Those customization options provide a very powerful means for the authors of types to control how their type is iterated.
If the object is a numerical collection and exposes
the traditional length
property and item
function,
then a collection iterator will be used.
As a fallback for all objects, an iterator that does property enumeration is used.
When a type wants to customize it's iteration, it doesn't need to implement this break out or result manipulation functionality itself, instead the calling code can be assured of a standard interface for this functionality.
If you are creating a numerical collection type then iteration is provided by default after you implement the collection interface, making this the simplest option for specifying iteration.
There are two things required for a numerical collection
type to be iterable. It must have a length
property,
and it must have an item
function which accepts a position
and returns the element at that position.
Once this is done, the iterator system automatically
knows how to iterate the object.
If you wish to customize the iteration then you can specify
an each
function, which is obviously also used in the case of
non-numerical collection types.
The simplest means to fully customize iteration is by implementing an each iterator on the object or prototype. If you are creating an iterable object, this is the standard way to implement it's iteration.
The each
function will be called by the iterator that
the user is executing.
It must accepts two parameters:
1. The user-given iteration function
2. The iterator-created iteration object
The each
function must do the following:
1. Call the iteration function for each element
2. Pass in the following to the iteration function:
(elementValue,
elementKey,
collectionObject,
iterationObject)
3. Return the value of the last iteration to the caller
The iteration object that needs to be passed to the user function holds on it a method that allows the user to stop the iteration, and to manipulate the result of the iteration.
MyType.prototype.each = function(F, iter) {
var R;
for (var i = 0; i < this.length; i++)
R = F(this[i], i, this, iter);
return R;
}
Jay provides the following iterators that you can access directly, listed in the order of preference that iterator
uses:
- EachIterator
- NumberIterator
- StringIterator
- ArrayIterator
- CollectionIterator
- PropertyIterator
There are two extra iterators also available directly,
which are not used at all by iterator
,
these are KeyIterator
and ValueIterator
.
The key iterator will iterate through the keys of a collection as though the iterator was an array with the keys as the values. The value iterator, perhaps a lot less useful,
will iterate through the values of a collection as though
the iterator was an array with the values as the values,
and the keys of the values will be consecutive numbers starting from zero, as opposed to the actual keys of the collection.
These two extra iterators work on any object that implements an each
function. They are extremely useful for creating "lazy"
iterables, as in you can pass them to some other code
that perhaps doesn't need to know that the collection you passed them is just a filtered down lazy iteration of some
potentially large collection.
Another very useful addition to the iteration family is not
an iterator, but rather an iterable type. The Range
type
stores an incremental range with a start, end and increment.
This is a very nice way to iterate over a large range without
actually creating and storing all the values in the range.
With Javascript being a functional programming language, we have - as you've seen - used functions as the iteration blocks. Functions of course have the ability to return values, and this gives iteration functions the ability to do some powerful things.
The foreach
construct allows the caller to return a
result value from the iteration.
The iteration function is obviously invoked many times,
but the foreach
can of course only return one result,
it will always return the result of the last invocation of the iteration function.
// only returns the last element of the array
var double = foreach(array, function(v) {
return v;
})
With an extra functional programming utility that Jay provides,
we are able to return an array of the values returned
by all the invocations of the iteration function.
This is a popular programming functionality known as
as list comprehension. Jay provides the collector
utility
which you can wrap a function in to make a "collector function" outer function.
The collector function will collect all the value returned
by the inner function, and each time the collector function returns a value it returns this array of all the values it has so far collected.
With this we are able to map the values of one object into
corresponding values that make up another array.
The following creates a new array where each value is twice
it's corresponding value in the original array.
var doubles = foreach(array, collector(function(v) {
return v * 2
}))
If you want an element to be excluded from the resulting array, then you simply don't return anything. The resulting array will be shorter as a result of not including a result for that iteration. If you want to include a void value without making the array shorter then you can return null. Being able to exclude elements makes a collector function a powerful way to filter an array into a new array.
If we want to quickly create an array that contains every 5th integer from 50 to 100 we can use Jay's Range type with a list comprehension.
var numbers = foreach(new Range(50,100,5), collector(function(v) { return v; }))
Aside from simply returning values from the iteration, it is also possible to directly manipulate the one result object that will be returned at the end of all the iterations. Instead of using a collector we can directly access the final result. This is very useful when you want the iteration functions to manipulate the result in a special manner. Creating an object property to value map is an especially common requirement that can be achieved with this technique.
The result of the previous iteration is stored on the
result
property of the iteration object that the user function receives.
By default the final result is set to a plain object,
and the iteration function would have to return a non-undefined
value in order to overwrite this, so it is safe to assume
their is an existing result object.
The following will reverse the array index to value mapping, giving an object where the array's value's point to the position that they occur in the array.
var positions = foreach(array, function(v, k, a, it) {
it.result[v] = k
})
The built-in loop constructs all have the 'break' statement that can be used to exit the loop at any point. When using functions as iteration blocks the 'break' statement obviously does not work anymore. However, there is a means to effectively "break" out of functions, and that is by throwing exceptions.
The iterator system provides an implementation of break
functionality that is always available to users of
foreach
. The iteration object that is passed to the user
iteration function holds a stop
function on it,
which will break out of the loop by throwing an exception,
which the foreach
function will silently catch.
If you want to specify a return value while breaking out,
you can pass it to the stop function. Breaking out of a
loop looks like this:
foreach (object, function(val, key, obj, it) {
if (key > 10)
it.stop()
})
We can use the iteration result together with the break out functionality to indicate point at which a loop exited.
var biggy = foreach (array, function(val, key, arr, it) {
if (val > 20)
it.stop(key)
})
this section is still being written.
scope:
var jay = scope(function() {
// a read-only variable.
var VERSION = '1.0'
this.getVersion = function() {
return VERSION
}
})
namespacing:
namespace(global, 'jay', function(jay) {
// if the namespace already exists, a reference to it is returned, otherwise it is created.
namespace(jay, 'Iteration', function() {
jay.Iteration.msg = "p.s jay's iteration stuff isn't really in this namespace, this is just an example."
})
})
this section is still being written.
- Tame the complexity of asynchronous callbacks.
- Write several parallel functions as easily as parallel(function() {})
- Start continual(ly executing) functions as easily as continual(function() {})
- Control the rate at which each thread executes
- Pause threads, resume them, kill them.