169 lines
8.3 KiB
Plaintext
169 lines
8.3 KiB
Plaintext
|
@c This document is part of the ease.js manual
|
||
|
@c Copyright (c) 2011 Mike Gerwitz
|
||
|
@c Permission is granted to copy, distribute and/or modify this document
|
||
|
@c under the terms of the GNU Free Documentation License, Version 1.3
|
||
|
@c or any later version published by the Free Software Foundation;
|
||
|
@c with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
|
||
|
@c Texts. A copy of the license is included in the section entitled ``GNU
|
||
|
@c Free Documentation License''.
|
||
|
|
||
|
@node Implementation Details
|
||
|
@appendix Implementation Details / Rationale
|
||
|
The majority of the development time spent on ease.js was not hacking away at
|
||
|
the source code. Rather, it was spent with pen and paper. Every aspect of
|
||
|
ease.js was heavily planned from the start. Every detail was important to ensure
|
||
|
a consistent implementation that worked, was fast and that developers would
|
||
|
enjoy working with. Failures upfront or alterations to the design in later
|
||
|
versions would break backwards compatibility unnecessarily and damage the
|
||
|
reputation of the project.
|
||
|
|
||
|
When using ease.js, developers may wonder why things were implemented in the
|
||
|
manner that they were. Perhaps they have a problem with the implementation, or
|
||
|
just want to learn how the project works. This project was an excellent learning
|
||
|
experience that deals very closely with the power and flexibility of prototypal
|
||
|
programming. In an attempt to appease both parties, this appendix is provided to
|
||
|
provide some details and rationale behind ease.js.
|
||
|
|
||
|
@menu
|
||
|
* Class Module Design::
|
||
|
@end menu
|
||
|
|
||
|
|
||
|
@node Class Module Design
|
||
|
@section Class Module Design
|
||
|
The @var{Class} module, which is accessible via @samp{require( 'easejs'
|
||
|
).Class}, is the backbone of the entire project. In a class-based
|
||
|
Object-Oriented model, as one could guess by the name, the class is the star
|
||
|
player. When the project began, this was the only initial implementation detail.
|
||
|
Everything else was later layered atop of it.
|
||
|
|
||
|
As such, developing the Class module took the most thought and presented the
|
||
|
largest challenge throughout the project. Every detail of its implementation
|
||
|
exists for a reason. Nothing was put in place because the author simply ``felt
|
||
|
like it''. The project aims to exist as a strong, reliable standard for the
|
||
|
development of JavaScript-based applications. If such a goal is to be attained,
|
||
|
the feature set and implementation details would have to be strongly functional,
|
||
|
easy to use and make sense to the Object-Oriented developer community.
|
||
|
|
||
|
The design also requires a strong understanding of Object-Oriented development.
|
||
|
Attention was paid to the nuances that could otherwise introduce bugs or an
|
||
|
inconsistent implementation.
|
||
|
|
||
|
@menu
|
||
|
* Class Declaration Syntax::
|
||
|
* Class Storage::
|
||
|
* Constructor Implementation::
|
||
|
@end menu
|
||
|
|
||
|
@node Class Declaration Syntax
|
||
|
@subsection Class Declaration Syntax
|
||
|
Much thought was put into how a class should be declared. The chosen style
|
||
|
serves as syntatic sugar, making the declarations appear very similar to classes
|
||
|
in other Object-Oriented languages.
|
||
|
|
||
|
The original style was based on John Resig's blog post about a basic means of
|
||
|
extending class-like objects (@pxref{About}). That style was
|
||
|
@samp{Class.extend()} to declare a new class and @samp{Foo.extend()} to extend
|
||
|
an existing class. This implementation is still supported for creating anonymous
|
||
|
classes. However, a means needed to be provided to create named classes. In
|
||
|
addition, invoking @code{extend()} on an empty class seemed unnecessary.
|
||
|
|
||
|
The next incarnation made the @var{Class} module invokable. Anonymous classes
|
||
|
could be defined using @samp{Class( @{@} )} and named classes could be defined
|
||
|
by passing in a string as the first argument: @samp{Class( 'Foo', @{@} )}.
|
||
|
Classes could still be extended using the previously mentioned syntax, but that
|
||
|
did no justice if we need to provide a class name. Therefore, the @samp{Class(
|
||
|
'SubFoo' ).extend( Supertype, @{@} )} syntax was also adopted.
|
||
|
|
||
|
JavaScript's use of curly braces to represent objects provides a very convenient
|
||
|
means of making class definitions look like actual class definitions. By
|
||
|
convention, the opening brace for the declaration object is on its own line, to
|
||
|
make it look like an opening block.
|
||
|
|
||
|
@float Figure, f:class-def-syntax
|
||
|
@verbatim
|
||
|
Class( 'Foo' )
|
||
|
.implement( Bar )
|
||
|
.extend(
|
||
|
{
|
||
|
'public foo': function()
|
||
|
{
|
||
|
}
|
||
|
} );
|
||
|
@end verbatim
|
||
|
@caption{Syntax and style of class definition}
|
||
|
@end float
|
||
|
|
||
|
Syntax for implementing interfaces and extending classes was another
|
||
|
consideration. The implementation shown above was chosen for a couple of
|
||
|
reasons. Firstly, verbs were chosen in order to (a) prevent the use of reserved
|
||
|
words and (b) to represent that the process was taking place at @emph{runtime},
|
||
|
@emph{as} the code was being executed. Unlike a language like C++ or Java, the
|
||
|
classes are not prepared at compile-time.
|
||
|
|
||
|
@node Class Storage
|
||
|
@subsection Class Storage
|
||
|
One of the more powerful features of ease.js is how classes (and other objects,
|
||
|
such as Interfaces) are stored. Rather than adopting its own model, the decision
|
||
|
was instead to blend into how JavaScript already structures its data. Everything
|
||
|
in JavaScript can be assigned to a variable, including functions. Classes are no
|
||
|
different.
|
||
|
|
||
|
One decision was whether or not to store classes internally by name, then permit
|
||
|
accessing it globally (wherever ease.js is available). This is how most
|
||
|
Object-Oriented languages work. If the file in which the class is defined is
|
||
|
available, the class can generally be referenced by name. This may seem natural
|
||
|
to developers coming from other Object-Oriented languages. The decision was to
|
||
|
@emph{not} adopt this model.
|
||
|
|
||
|
By storing classes @emph{only} in variables, we have fine control over the
|
||
|
scope and permit the developer to adopt their own mechanism for organizing their
|
||
|
classes. For example, if the developer wishes to use namespacing, then he/she is
|
||
|
free to assign the class to a namespace (e.g. @samp{org.foo.my.ns.Foo =
|
||
|
Class( @{@} )}). More importantly, we can take advantage of the CommonJS format
|
||
|
that ease.js was initially built for by assigning the class to
|
||
|
@code{module.exports}. This permits @samp{require( 'filename' )} to return the
|
||
|
class.
|
||
|
|
||
|
This method also permits defining anonymous classes (while not necessarily
|
||
|
recommended, they have their uses just as anonymous functions do), mimic the
|
||
|
concept of Java's inner classes and create temporary classes (@pxref{Temporary
|
||
|
Classes}). Indeed, we can do whatever scoping that JavaScript permits.
|
||
|
|
||
|
@subsubsection Memory Management
|
||
|
Memory management is perhaps one of the most important considerations.
|
||
|
Initially, ease.js encapsulated class metadata and visibility structures.
|
||
|
However, it quickly became apparent that this method of storing data, although
|
||
|
excellent for protecting data from being manipulated, caused what appeared to be
|
||
|
memory leaks in long-running software. These were in fact not memory leaks, but
|
||
|
ease.js was keeping references to class data with no idea when to free them.
|
||
|
|
||
|
If the globally accessible model would have been adopted (storing classes
|
||
|
internally by class name rather than in variables), classes would not have been
|
||
|
freed from memory when they went out of scope. This raises the memory footprint
|
||
|
unnecessarily, especially for temporary classes. It would make sense that, after
|
||
|
a temporary class is done being used, that the class be freed from memory.
|
||
|
|
||
|
Given this fact alone, the author firmly believes that the model that was chosen
|
||
|
was the best choice.
|
||
|
|
||
|
@node Constructor Implementation
|
||
|
@subsection Constructor Implementation
|
||
|
ease.js uses a PHP-style constructor. Rather than using the class name as the
|
||
|
constructor, a @code{__construct()} method is used. This was chosen primarily
|
||
|
because ease.js does not always know the name of the class. In fact, in the
|
||
|
early stages of development, named classes were unsupported. With the PHP-style
|
||
|
constructor, the class name does not need to be known, allowing constructors to
|
||
|
be written for anonymous and named classes alike.
|
||
|
|
||
|
In addition, the PHP-style constructor is consistent between class definitions.
|
||
|
To look up a constructor, one need only search for ``__construct'', rather than
|
||
|
the class name. This makes certain operations, such as global searching (using
|
||
|
@command{grep} or any other utility), much simpler.
|
||
|
|
||
|
The constructor is optional. If one is not provided, nothing is done after the
|
||
|
class is instantiated (aside from the internal ease.js initialization tasks).
|
||
|
|
||
|
The constructor is called after all initialization tasks have been completed.
|
||
|
|