From 4b871dabc8b6c3b9da17199b2ee46ed3656b25ef Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Fri, 17 Jan 2014 22:27:47 -0500 Subject: [PATCH] Corrected all line lengths in doc/ Now conforms to tools/linechk's 76-char limitation. --- README.md | 2 +- doc/Makefile.am | 9 +- doc/README | 8 +- doc/about.texi | 226 ++--- doc/classes.texi | 1016 +++++++++++----------- doc/easejs.css | 2 +- doc/easejs.texi | 10 +- doc/img/visobj-collection-wide.txt | 30 +- doc/impl-details.texi | 1303 ++++++++++++++-------------- doc/integration.texi | 146 ++-- doc/interactive.js | 13 +- doc/license.texi | 10 +- doc/mkeywords.texi | 315 +++---- doc/source-tree.texi | 159 ++-- 14 files changed, 1681 insertions(+), 1568 deletions(-) diff --git a/README.md b/README.md index e0b3079..efc8c8e 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Current support includes: * Classical inheritance * Abstract classes and methods * Interfaces -* Visibility (public, protected and private members) +* Visibility (public, protected, and private members) * Static and constant members While the current focus of the project is object-oriented design, it is likely diff --git a/doc/Makefile.am b/doc/Makefile.am index 7e3879b..3c937fb 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -67,8 +67,8 @@ img/%.png: img/%.dia $(path_doc_output_plain): $(doc_imgs_txt) makeinfo --plain -I "." "${path_easejs_texi}" -o "$@" -# doc html (multiple pages); override default target to call automake's first -# and then post-process output (it has no *-hook target support) +# doc html (multiple pages); override default target to call automake's +# first and then post-process output (it has no *-hook target support) html: html-local html-am $(path_doc_output_html)/$(path_doc_css_ref) sed -i '$(doc_replace)' easejs.html/*.htm? @@ -76,7 +76,7 @@ html: html-local html-am $(path_doc_output_html)/$(path_doc_css_ref) html-single: $(path_doc_output_html1) $(path_doc_output_html1): html-local $(MAKEINFO) --no-split --html --css-include="${path_doc_css}" \ - -I "." -o "$(path_doc_output_html1)" "${path_easejs_texi}" \ + -I . -o "$(path_doc_output_html1)" "${path_easejs_texi}" \ && sed -i '$(doc_replace)' "$(path_doc_output_html1)" # doc images (in build dir) @@ -103,7 +103,8 @@ img: $(doc_imgs) plain: $(path_doc_output_plain) install-plain: plain install -d $(DESTDIR)$(docdir) - install -m644 $(path_doc_output_plain) $(DESTDIR)$(docdir)/easejs.txt + install -m644 $(path_doc_output_plain) \ + $(DESTDIR)$(docdir)/easejs.txt clean-local: rm -rf $(path_doc_img)/*.png diff --git a/doc/README b/doc/README index 521c334..4d65b5f 100644 --- a/doc/README +++ b/doc/README @@ -2,8 +2,8 @@ The ease.js website uses 3rd-party scripts, described below. highlight.js ------------ -Used for syntax highlighting on both the website itself and in the manual. Uses -the modified BSD license. +Used for syntax highlighting on both the website itself and in the manual. +Uses the modified BSD license. Copyright (c) 2006, Ivan Sagalaev All rights reserved. @@ -27,5 +27,5 @@ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/doc/about.texi b/doc/about.texi index 16d276e..b1dd5ba 100644 --- a/doc/about.texi +++ b/doc/about.texi @@ -1,18 +1,18 @@ @c This document is part of the GNU ease.js manual. -@c Copyright (C) 2011, 2013 Mike Gerwitz +@c Copyright (C) 2011, 2013, 2014 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''. +@c under the terms of the GNU Free Documentation License, Version 1.3 or +@c any later version published by the Free Software Foundation; with no +@c Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. +@c A copy of the license is included in the section entitled ``GNU Free +@c Documentation License''. @node About @unnumbered About GNU ease.js -GNU ease.js is a classical object-oriented framework for Javascript, intended to -eliminate boilerplate code and "ease" the transition into JavaScript from other -object-oriented languages. +GNU ease.js is a classical object-oriented framework for Javascript, +intended to eliminate boilerplate code and ``ease'' the transition into +JavaScript from other object-oriented languages. Current support includes: @itemize @bullet @@ -20,165 +20,171 @@ Current support includes: @item Classical inheritance @item Abstract classes and methods @item Interfaces -@item Visibility (public, protected and private members) -@item Static, constant and final members +@item Visibility (public, protected, and private members) +@item Static, constant, and final members @end itemize -While the current focus of the project is Object-Oriented design, it is likely -that ease.js will expand to other paradigms in the future. +While the current focus of the project is Object-Oriented design, it is +likely that ease.js will expand to other paradigms in the future. @section History -ease.js was initially developed for use at the author's place of employment in -order to move the familiar concept of object-oriented development over to +ease.js was initially developed for use at the author's place of employment +in order to move the familiar concept of object-oriented development over to JavaScript for use in robust web applications. JavaScript lacks basic core principals of object-oriented development, the most major of which is proper encapsulation. -The library would be required to work both server and client-side, supporting -all major web browsers as far back as Internet Explorer 6. Since it would be -used in a production system and would be used to develop a core business -application, it must also work flawlessly. This meant heavy unit testing. +The library would be required to work both server and client-side, +supporting all major web browsers as far back as Internet Explorer 6. Since +it would be used in a production system and would be used to develop a core +business application, it must also work flawlessly. This meant heavy unit +testing. The solution was to develop a library that would first work server-side. The software of choice for server-side JavaScript was @uref{http://nodejs.org, -Node.js}. Node uses the CommonJS format for modules. This provided an intuitive -means of modularizing the code without use of an Object Oriented development -style (the closest other option would be Prototypal). ease.js was first -developed to work on Node.js. +Node.js}. Node uses the CommonJS format for modules. This provided an +intuitive means of modularizing the code without use of an Object Oriented +development style (the closest other option would be Prototypal). ease.js +was first developed to work on Node.js. -Moving the code over to the browser is not a difficult concept, since the entire -library relied only on standard JavaScript. A couple important factors had to be -taken into account, mainly that CommonJS modules don't simply ``work'' -client-side without some type of wrapper, not all browsers support ECMAScript 5 -and the assertion system used for tests is a Node.js module. +Moving the code over to the browser is not a difficult concept, since the +entire library relied only on standard JavaScript. A couple important +factors had to be taken into account, mainly that CommonJS modules don't +simply ``work'' client-side without some type of wrapper, not all browsers +support ECMAScript 5 and the assertion system used for tests is a Node.js +module. This involved writing a simple script to concatenate all the modules and appropriately wrap them in closures, thereby solving the CommonJS issue. The required assertions were ported over to the client. The only issue was then -ECMAScript 5 support, which with a little thought, the browser could gracefully -fall back on by sacrificing certain features but leaving the core functionality -relatively unscathed. This provides a proper cross-browser implementation and, -very importantly, allows the unit tests to be run both server and client side. -One can then be confident that ease.js will operate on both the server and a -wide range of web browsers without having to maintain separate tests for each. +ECMAScript 5 support, which with a little thought, the browser could +gracefully fall back on by sacrificing certain features but leaving the core +functionality relatively unscathed. This provides a proper cross-browser +implementation and, very importantly, allows the unit tests to be run both +server and client side. One can then be confident that ease.js will operate +on both the server and a wide range of web browsers without having to +maintain separate tests for each. -Needless to say, the development was successful and the project has been used in -production long before v0.1.0-pre was even conceived. It was thought at the -beginning of the project that versions would be unnecessary, due to its relative -simplicity and fairly basic feature set. The project has since evolved past its -original specification and hopes to introduce a number of exciting features in -the future. +Needless to say, the development was successful and the project has been +used in production long before v0.1.0-pre was even conceived. It was thought +at the beginning of the project that versions would be unnecessary, due to +its relative simplicity and fairly basic feature set. The project has since +evolved past its original specification and hopes to introduce a number of +exciting features in the future. -The project is owned and developed independently by Mike Gerwitz. There is no -ownership by his employer. +The project is owned and developed independently by Mike Gerwitz. There is +no ownership by his employer. -On 22 December 2013, ease.js officially became a part of GNU with the kind help -and supervision of Brandon Invergo. +On 22 December 2013, ease.js officially became a part of GNU with the kind +help and supervision of Brandon Invergo. @section Why ease.js? -There already exists a number of different ways to accomplish inheritance and -various levels of encapsulation in JavaScript. Why ease.js? Though a number of -frameworks did provide class-like definitions, basic inheritance and other minor -feature sets, none of them seemed to be an all-encompassing solution to providing -a strong framework for Object-Oriented development in JavaScript. +There already exists a number of different ways to accomplish inheritance +and various levels of encapsulation in JavaScript. Why ease.js? Though a +number of frameworks did provide class-like definitions, basic inheritance +and other minor feature sets, none of them seemed to be an all-encompassing +solution to providing a strong framework for Object-Oriented development in +JavaScript. -ease.js was first inspired by John Resig's post on "Simple JavasScript -Inheritance"@footnote{John's blog post is available at -@uref{http://ejohn.org/blog/simple-javascript-inheritance/}.}. This very basic -example provided a means to define a ``class'' and extend it. It used a -PHP-style constructor and was intuitive to use. Though it was an excellent +ease.js was first inspired by John Resig's post on ``Simple JavasScript +Inheritance''@footnote{John's blog post is available at +@uref{http://ejohn.org/blog/simple-javascript-inheritance/}.}. This very +basic example provided a means to define a ``class'' and extend it. It used +a PHP-style constructor and was intuitive to use. Though it was an excellent alternative to defining and inheriting classes by working directly with prototypes, it was far from a solid solution. It lacked abstract methods, -interfaces, encapsulation (visibility) and many other important features. +interfaces, encapsulation (visibility), and many other important features. Another solution was needed. -Using John's example as a base concept, ease.js was developed to address those -core issues. Importantly, the project needed to fulfill the following goals: +Using John's example as a base concept, ease.js was developed to address +those core issues. Importantly, the project needed to fulfill the following +goals: @table @strong @item Intuitive Class Definitions Users of Object-Oriented languages are used to a certain style of class -declaration that is fairly consistent. Class definitions within the framework -should be reflective of this. A programmer familiar with Object-Oriented -development should be able to look at the code and clearly see what the class is -doing and how it is defined. +declaration that is fairly consistent. Class definitions within the +framework should be reflective of this. A programmer familiar with +Object-Oriented development should be able to look at the code and clearly +see what the class is doing and how it is defined. @item Encapsulation -The absolute most important concept that ease.js wished to address was that of -encapsulation. Encapsulation is one of the most important principals of +The absolute most important concept that ease.js wished to address was that +of encapsulation. Encapsulation is one of the most important principals of Object-Oriented development. This meant implementing a system that would not only support public and private members (which can be done conventionally in JavaScript through ``privileged members''), but must also support -@emph{protected} members. Protected members have long been elusive to JavaScript -developers. +@emph{protected} members. Protected members have long been elusive to +JavaScript developers. @item Interfaces / Abstract Classes -Interfaces and Abstract Classes are a core concept and facilitate code reuse and -the development of consistent APIs. They also prove to be very useful for -polymorphism. Without them, we must trust that the developer has implemented the -correct API. If not, it will likely result in confusing runtime errors. -We also cannot ensure an object is passed with the expected API through the use -of polymorphism. +Interfaces and Abstract Classes are a core concept and facilitate code reuse +and the development of consistent APIs. They also prove to be very useful +for polymorphism. Without them, we must trust that the developer has +implemented the correct API. If not, it will likely result in confusing +runtime errors. We also cannot ensure an object is passed with the expected +API through the use of polymorphism. @item Inheritance Basic inheritance can be done through use of prototype chains. However, the -above concepts introduce additional complications. Firstly, we must be able to -implement interfaces. A simple prototype chain cannot do this (an object cannot -have multiple prototypes). Furthermore, protected members must be inherited by -subtypes, while making private members unavailable. In the future, when traits -are added to the mix, we run into the same problem as we do with interfaces. +above concepts introduce additional complications. Firstly, we must be able +to implement interfaces. A simple prototype chain cannot do this (an object +cannot have multiple prototypes). Furthermore, protected members must be +inherited by subtypes, while making private members unavailable. In the +future, when traits are added to the mix, we run into the same problem as we +do with interfaces. @item CommonJS, Server and Client -The framework would have to be used on both the server and client. Server-side, -@uref{http://nodejs.org, Node.js} was chosen. It used a CommonJS format for -modules. In order to get ease.js working client side, it would have to be -wrapped in such a way that the code could remain unchanged and still operate the -same. Furthermore, all tests written for the framework would have to run both -server and client-side, ensuring a consistent experience on the server and -across all supported browsers. Support would have to go as far back as Internet -Explorer 6 to support legacy systems. +The framework would have to be used on both the server and client. +Server-side, @uref{http://nodejs.org, Node.js} was chosen. It used a +CommonJS format for modules. In order to get ease.js working client side, it +would have to be wrapped in such a way that the code could remain unchanged +and still operate the same. Furthermore, all tests written for the framework +would have to run both server and client-side, ensuring a consistent +experience on the server and across all supported browsers. Support would +have to go as far back as Internet Explorer 6 to support legacy systems. @item Performance Everyone knows that Object-Oriented programming incurs a performance hit in return for numerous benefits. ease.js is not magic; it too would incur a -performance it. This hit must be low. Throughout the entire time the software is -running, the hit must be low enough that it is insignificant (less than 1% of -the total running time). This applies to any time the framework is used - from -class creation to method invocation. +performance it. This hit must be low. Throughout the entire time the +software is running, the hit must be low enough that it is insignificant +(less than 1% of the total running time). This applies to any time the +framework is used - from class creation to method invocation. @item Quality Design A quality design for the system is important for a number of reasons. This includes consistency with other languages and performance considerations. It -must also be easily maintainable and extensible. Object-Oriented programming is -all about @emph{restricting} what the developer can do. It is important to do so -properly and ensure it is consistent with other languages. If something is -inconsistent early on, and that inconsistency is adopted throughout a piece of -software, fixing the inconsistency could potentially result in breaking the -software. +must also be easily maintainable and extensible. Object-Oriented programming +is all about @emph{restricting} what the developer can do. It is important +to do so properly and ensure it is consistent with other languages. If +something is inconsistent early on, and that inconsistency is adopted +throughout a piece of software, fixing the inconsistency could potentially +result in breaking the software. @item Heavily Tested -The framework would be used to develop critical business applications. It needed -to perform flawlessly. A bug could potentially introduce flaws into the entire -system. Furthermore, bugs in the framework could create a debugging nightmare, -with developers wondering if the flaw exists in their own software or the -framework. This is a framework that would be very tightly coupled with the -software built atop of it. In order to ensure production quality, the framework -would have to be heavily tested. As such, a test-driven development cycle is -preferred. +The framework would be used to develop critical business applications. It +needed to perform flawlessly. A bug could potentially introduce flaws into +the entire system. Furthermore, bugs in the framework could create a +debugging nightmare, with developers wondering if the flaw exists in their +own software or the framework. This is a framework that would be very +tightly coupled with the software built atop of it. In order to ensure +production quality, the framework would have to be heavily tested. As such, +a test-driven development cycle is preferred. @item Well Documented The framework should be intuitive enough that documentation is generally unneeded, but in the event the developer does need help in implementing the -framework in their own software, the help should be readily available. Wasting -time attempting to figure out the framework is both frustrating and increases -project cost. +framework in their own software, the help should be readily available. +Wasting time attempting to figure out the framework is both frustrating and +increases project cost. @end table -The above are the main factors taken into consideration when first developing -ease.js. There were no existing frameworks that met all of the above criteria. -Therefore, it was determined that ease.js was a valid project that addressed -genuine needs for which there was no current, all-encompassing solution. - +The above are the main factors taken into consideration when first +developing ease.js. There were no existing frameworks that met all of the +above criteria. Therefore, it was determined that ease.js was a valid +project that addressed genuine needs for which there was no current, +all-encompassing solution. diff --git a/doc/classes.texi b/doc/classes.texi index 7800fbb..b218b18 100644 --- a/doc/classes.texi +++ b/doc/classes.texi @@ -1,28 +1,30 @@ @c This document is part of the GNU ease.js manual. @c Copyright (C) 2011, 2012, 2013, 2014 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''. +@c under the terms of the GNU Free Documentation License, Version 1.3 or +@c any later version published by the Free Software Foundation; with no +@c Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. +@c A copy of the license is included in the section entitled ``GNU Free +@c Documentation License''. @node Classes @chapter Working With Classes -In Object-Oriented programming, the most common term you are likely to encounter -is ``Class''. A @dfn{class} is like a blueprint for creating an @dfn{object}, -which is an @dfn{instance} of that class. Classes contain @dfn{members}, which -include primarily properties and methods. A @dfn{property} is a value, much like -a variable, that a class ``owns''. A @dfn{method}, when comparing with -JavaScript, is a function that is ``owned'' by a class. As a consequence, -properties and methods are not part of the global scope. +In Object-Oriented programming, the most common term you are likely to +encounter is ``Class''. A @dfn{class} is like a blueprint for creating an +@dfn{object}, which is an @dfn{instance} of that class. Classes contain +@dfn{members}, which include primarily properties and methods. A +@dfn{property} is a value, much like a variable, that a class ``owns''. A +@dfn{method}, when comparing with JavaScript, is a function that is +``owned'' by a class. As a consequence, properties and methods are not part +of the global scope. -JavaScript does not support classes in the manner traditionally understood by -Object-Oriented programmers. This is because JavaScript follows a different -model which instead uses prototypes. Using this model, JavaScript supports -basic instantiation and inheritance. Rather than instantiating classes, -JavaScript instantiates constructors, which are functions. The following example -illustrates how you would typically create a class-like object in JavaScript: +JavaScript does not support classes in the manner traditionally understood +by Object-Oriented programmers. This is because JavaScript follows a +different model which instead uses prototypes. Using this model, JavaScript +supports basic instantiation and inheritance. Rather than instantiating +classes, JavaScript instantiates constructors, which are functions. The +following example illustrates how you would typically create a class-like +object in JavaScript: @float Figure, f:class-js @verbatim @@ -49,30 +51,31 @@ illustrates how you would typically create a class-like object in JavaScript: @caption{Basic ``Class'' in JavaScript @emph{without} using ease.js} @end float -This gets the job done, but the prototypal paradigm has a number of limitations -amongst its incredible flexibility. For Object-Oriented programmers, it's both -alien and inadequate. That is not to say that it is not useful. In fact, it is -so flexible that an entire Object-Oriented framework was able to be built atop -of it. +This gets the job done, but the prototypal paradigm has a number of +limitations amongst its incredible flexibility. For Object-Oriented +programmers, it's both alien and inadequate. That is not to say that it is +not useful. In fact, it is so flexible that an entire Object-Oriented +framework was able to be built atop of it. ease.js aims to address the limitations of the prototype model and provide a -familiar environment for Object-Oriented developers. Developers should not have -to worry about @emph{how} classes are implemented in JavaScript (indeed, those -details should be encapsulated). You, as a developer, should be concerned with -only how to declare and use the classes. If you do not understand what a -prototype is, that should be perfectly fine. You shouldn't need to understand it -in order to use the library (though, it's always good to understand what a -prototype is when working with JavaScript). +familiar environment for Object-Oriented developers. Developers should not +have to worry about @emph{how} classes are implemented in JavaScript +(indeed, those details should be encapsulated). You, as a developer, should +be concerned with only how to declare and use the classes. If you do not +understand what a prototype is, that should be perfectly fine. You shouldn't +need to understand it in order to use the library (though, it's always good +to understand what a prototype is when working with JavaScript). -In this chapter and those that follow, we will see the limitations that ease.js -addresses. We will also see how to declare the classes using both prototypes and -ease.js, until such a point where prototypes are no longer adequate. +In this chapter and those that follow, we will see the limitations that +ease.js addresses. We will also see how to declare the classes using both +prototypes and ease.js, until such a point where prototypes are no longer +adequate. @menu * Defining Classes:: Learn how to define a class with ease.js * Inheritance:: Extending classes from another * Static Members:: Members whose use do not require instantiation -* Abstract Members:: Declare members, deferring their definition to subtypes +* Abstract Members:: Declare members, deferring definition to subtypes * Method Proxies:: Methods that proxy calls to another object @end menu @@ -93,34 +96,35 @@ Define anonymous class @var{C} as described by @var{dfn}. Define anonymous class @var{C} as described by @var{dfn}. @end table -Class @var{C} can be defined in a number of manners, as listed above, provided -a @dfn{definition object} @var{dfn} containing the class members and options. An -optional string @var{name} may be provided to set an internal identifier for -@var{C}, which may be used for reflection and error messages. If @var{name} is -omitted, @var{C} will be declared anonymous. +Class @var{C} can be defined in a number of manners, as listed above, +provided a @dfn{definition object} @var{dfn} containing the class members +and options. An optional string @var{name} may be provided to set an +internal identifier for @var{C}, which may be used for reflection and error +messages. If @var{name} is omitted, @var{C} will be declared anonymous. -@code{Class} must be imported (@pxref{Including}) from @code{easejs.Class}; it -is not available in the global scope. +@code{Class} must be imported (@pxref{Including}) from @code{easejs.Class}; +it is not available in the global scope. @anchor{dfnobj} @subsection Definition Object @table @code @item dfn = @{ '[@var{keywords}] @var{name}': @var{value}[, ...] @} -Define definition object @var{dfn} containing a member identified by @var{name}, -described by optional @var{keywords} with the value of @var{value}. The member -type is determined by @code{typeof} @var{value}. Multiple members may be -provided in a single definition object. +Define definition object @var{dfn} containing a member identified by +@var{name}, described by optional @var{keywords} with the value of +@var{value}. The member type is determined by @code{typeof} @var{value}. +Multiple members may be provided in a single definition object. @end table The definition object @var{dfn} has the following properties: @enumerate @item -The keys represent the @dfn{member declaration}, which may optionally contain -one or more @var{keywords} delimited by spaces. A space must delimit the final -keyword and @var{name}. +The keys represent the @dfn{member declaration}, which may optionally +contain one or more @var{keywords} delimited by spaces. A space must delimit +the final keyword and @var{name}. @enumerate @item - @var{keywords} must consist only of recognized tokens, delimited by spaces. + @var{keywords} must consist only of recognized tokens, delimited by + spaces. @item Each token in @var{keywords} must be unique per @var{name}. @@ -131,9 +135,9 @@ The @var{value} represents the @dfn{member definition}, the type of which determines what type of member will be declared. @enumerate @item - A @var{value} of type @code{function} will define a @dfn{method}, which is an - invokable member whose context is assigned to the class or class instance - depending on @var{keywords}. + A @var{value} of type @code{function} will define a @dfn{method}, which is + an invokable member whose context is assigned to the class or class + instance depending on @var{keywords}. @item All other types of @var{value} will define a @dfn{property} - a mutable @@ -159,7 +163,8 @@ For any member @var{name}: See @ref{Member Keywords,,Member Keywords} for @var{keywords} restrictions. @end itemize -For any member @var{name} declared as a @emph{method}, the following must hold +For any member @var{name} declared as a @emph{method}, the following must +hold true: @itemize @item @@ -168,27 +173,28 @@ true: @var{name} (@pxref{Inheritance}). @item @var{keywords} of member @var{name} may not contain both -@ref{Member Keywords,,@code{static}} and @ref{Member Keywords,,@code{virtual}} -keywords (@pxref{Static Members} and @ref{Inheritance}). +@ref{Member Keywords,,@code{static}} and @ref{Member +Keywords,,@code{virtual}} keywords (@pxref{Static Members} and +@ref{Inheritance}). @item @var{keywords} of member @var{name} may not contain the @ref{Member Keywords,,@code{const}} keyword. @item For any member @var{name} that contains the keyword -@ref{t:keywords,,@code{abstract}} in @var{keywords}, class @var{C} must instead -be declared as an @code{AbstractClass} (@pxref{Abstract Classes}). +@ref{t:keywords,,@code{abstract}} in @var{keywords}, class @var{C} must +instead be declared as an @code{AbstractClass} (@pxref{Abstract Classes}). @end itemize @subsection Discussion -In @ref{f:class-js}, we saw how one would conventionally declare a class-like -object (a prototype) in JavaScript. This method is preferred for many -developers, but it is important to recognize that there is a distinct difference -between Prototypal and Classical Object-Oriented development models. Prototypes -lack many of the conveniences and features that are provided by Classical -languages, but they can be emulated with prototypes. As an Object-Oriented -developer, you shouldn't concern yourself with @emph{how} a class is declared in -JavaScript. In true OO fashion, that behavior should be encapsulated. With -ease.js, it is. +In @ref{f:class-js}, we saw how one would conventionally declare a +class-like object (a prototype) in JavaScript. This method is preferred for +many developers, but it is important to recognize that there is a distinct +difference between Prototypal and Classical Object-Oriented development +models. Prototypes lack many of the conveniences and features that are +provided by Classical languages, but they can be emulated with prototypes. +As an Object-Oriented developer, you shouldn't concern yourself with +@emph{how} a class is declared in JavaScript. In true OO fashion, that +behavior should be encapsulated. With ease.js, it is. Let's take a look at how to declare that exact same class using ease.js: @@ -213,48 +219,49 @@ Let's take a look at how to declare that exact same class using ease.js: @caption{Basic anonymous class declaration using ease.js} @end float -That should look much more familiar to Object-Oriented developers. There are a -couple important notes before we continue evaluating this example: +That should look much more familiar to Object-Oriented developers. There are +a couple important notes before we continue evaluating this example: @itemize @item -The first thing you will likely notice is our use of the @code{public} keyword. -This is optional (the default visibility is public), but always recommended. -Future versions of ease.js may provide warnings when the visibility is omitted. -We will get more into visibility later on. +The first thing you will likely notice is our use of the @code{public} +keyword. This is optional (the default visibility is public), but always +recommended. Future versions of ease.js may provide warnings when the +visibility is omitted. We will get more into visibility later on. @item Unlike @ref{f:class-js,}, we do not use the @code{new} keyword in order to -instantiate our class. You are more than welcome to use the @code{new} keyword -if you wish, but it is optional when using ease.js. This is mainly because -without this feature, if the keyword is omitted, the constructor is called as a -normal function, which could have highly negative consequences. This style of -instantiation also has its benefits, which will be discussed later on. +instantiate our class. You are more than welcome to use the @code{new} +keyword if you wish, but it is optional when using ease.js. This is mainly +because without this feature, if the keyword is omitted, the constructor is +called as a normal function, which could have highly negative consequences. +This style of instantiation also has its benefits, which will be discussed +later on. @item -ease.js's class module is imported using @code{require()} in the above example. -If using ease.js client-side (@pxref{Client-Side Include}), you can instead use -@samp{var Class = easejs.Class}. From this point on, importing the module will -not be included in examples. +ease.js's class module is imported using @code{require()} in the above +example. If using ease.js client-side (@pxref{Client-Side Include}), you +can instead use @samp{var Class = easejs.Class}. From this point on, +importing the module will not be included in examples. @end itemize The above example declares an anonymous class, which is stored in the -variable @var{MyClass}. By convention, we use CamelCase, with the first letter -capital, for class names (and nothing else). +variable @var{MyClass}. By convention, we use CamelCase, with the first +letter capital, for class names (and nothing else). @menu * Anonymous vs. Named Classes:: -* Constructors:: How to declare a constructor -* Temporary Classes:: Throwaway classes that only need to be used once -* Temporary Instances:: Throwaway instances that only need to be used once +* Constructors:: How to declare a constructor +* Temporary Classes:: Throwaway classes that only need to be used once +* Temporary Instances:: Throwaway instances that only need to be used once @end menu @node Anonymous vs. Named Classes @subsection Anonymous vs. Named Classes -We state that @ref{f:class-easejs,} declared an @dfn{anyonmous class} because -the class was not given a name. Rather, it was simply assigned to a variable, -which itself has a name. To help keep this idea straight, consider the common -act of creating anonymous functions in JavaScript: +We state that @ref{f:class-easejs,} declared an @dfn{anyonmous class} +because the class was not given a name. Rather, it was simply assigned to a +variable, which itself has a name. To help keep this idea straight, consider +the common act of creating anonymous functions in JavaScript: @float Figure, f:anon-func @verbatim @@ -267,10 +274,11 @@ act of creating anonymous functions in JavaScript: @caption{Anonymous functions in JavaScript} @end float -If the function itself is not given a name, it is considered to be anonymous, -even though it is stored within a variable. Just as the engine has no idea what -that function is named, ease.js has no idea what the class is named because it -does not have access to the name of the variable to which it was assigned. +If the function itself is not given a name, it is considered to be +anonymous, even though it is stored within a variable. Just as the engine +has no idea what that function is named, ease.js has no idea what the class +is named because it does not have access to the name of the variable to +which it was assigned. Names are not required for classes, but they are recommended. For example, consider what may happen when your class is output in an error message. @@ -285,17 +293,17 @@ consider what may happen when your class is output in an error message. @caption{Anonymous classes do not make for useful error messages} @end float -If you have more than a couple classes in your software, that error message is -not too much help. You are left relying on the stack trace to track down the -error. This same output applies to converting a class to a string or viewing it -in a debugger. It is simply not helpful. If anything, it is confusing. If you've -debugged large JS applications that make liberal use of anonymous functions, you -might be able to understand that frustration. +If you have more than a couple classes in your software, that error message +is not too much help. You are left relying on the stack trace to track down +the error. This same output applies to converting a class to a string or +viewing it in a debugger. It is simply not helpful. If anything, it is +confusing. If you've debugged large JS applications that make liberal use of +anonymous functions, you might be able to understand that frustration. -Fortunately, ease.js permits you to declare a named class. A @dfn{named class} -is simply a class that is assigned a string for its name, so that error -messages, debuggers, etc provide more useful information. @emph{There is -functionally no difference between named and anonymous classes.} +Fortunately, ease.js permits you to declare a named class. A @dfn{named +class} is simply a class that is assigned a string for its name, so that +error messages, debuggers, etc provide more useful information. @emph{There +is functionally no difference between named and anonymous classes.} @float Figure, f:class-named @verbatim @@ -310,16 +318,16 @@ functionally no difference between named and anonymous classes.} @caption{Declaring an empty @emph{named} class} @end float -Much better! We now have a useful error message and immediately know which class -is causing the issue. +Much better! We now have a useful error message and immediately know which +class is causing the issue. @node Constructors @subsection Constructors -In JavaScript, we are used to functions themselves being a constructor because -only constructors can be instantiated. With ease.js, constructors are handled in -a manner similar to most other languages, by providing a separate method. The -implementation ease.js chose is very similar to that of PHP's -(@pxref{Constructor Implementation}). +In JavaScript, we are used to functions themselves being a constructor +because only constructors can be instantiated. With ease.js, constructors +are handled in a manner similar to most other languages, by providing a +separate method. The implementation ease.js chose is very similar to that of +PHP's (@pxref{Constructor Implementation}). @float Figure, f:constructor @verbatim @@ -340,12 +348,12 @@ implementation ease.js chose is very similar to that of PHP's @caption{Declaring constructors using ease.js} @end float -When the class is instantiated, the constructor is invoked, permitting you do to -any necessary initialization tasks before the class can be used. The constructor -operates exactly how you would expect a constructor to in JavaScript, with one -major difference. Returning an object in the constructor does @emph{not} return -that object instead of the new class instance, since this does not make sense in -a Class-based model. +When the class is instantiated, the constructor is invoked, permitting you +do to any necessary initialization tasks before the class can be used. The +constructor operates exactly how you would expect a constructor to in +JavaScript, with one major difference. Returning an object in the +constructor does @emph{not} return that object instead of the new class +instance, since this does not make sense in a Class-based model. If you wish to prevent a class from being instantiated, simply throw an exception within the constructor. This is useful if the class is intended to @@ -371,9 +379,9 @@ instantiated. @node Temporary Classes @subsection Temporary Classes In @ref{f:class-easejs,}, we saw that the @code{new} keyword was unnecessary -when instantiating classes. This permits a form of shorthand that is very useful -for creating @dfn{temporary classes}, or ``throwaway`` classes which are used -only once. +when instantiating classes. This permits a form of shorthand that is very +useful for creating @dfn{temporary classes}, or ``throwaway`` classes which +are used only once. Consider the following example: @@ -393,29 +401,29 @@ Consider the following example: @caption{Declaring a temporary (throwaway) class} @end float -In @ref{f:class-tmp,} above, rather than declaring a class, storing that in a -variable, then instantiating it separately, we are doing it in a single command. -Notice the parenthesis at the end of the statement. This invokes the -constructor. Since the @code{new} keyword is unnecessary, a new instance of the -class is stored in the variable @var{foo}. +In @ref{f:class-tmp,} above, rather than declaring a class, storing that in +a variable, then instantiating it separately, we are doing it in a single +command. Notice the parenthesis at the end of the statement. This invokes +the constructor. Since the @code{new} keyword is unnecessary, a new instance +of the class is stored in the variable @var{foo}. We call this a temporary class because it is used only to create a single -instance. The class is then never referenced again. Therefore, we needn't even -store it - it's throwaway. +instance. The class is then never referenced again. Therefore, we needn't +even store it - it's throwaway. -The downside of this feature is that it is difficult to notice unless the reader -is paying very close attention. There is no keyword to tip them off. Therefore, -it is very important to clearly document that you are storing an instance in the -variable rather than an actual class definition. If you follow the CamelCase -convention for class names, then simply do not capitalize the first letter of -the destination variable for the instance. +The downside of this feature is that it is difficult to notice unless the +reader is paying very close attention. There is no keyword to tip them off. +Therefore, it is very important to clearly document that you are storing an +instance in the variable rather than an actual class definition. If you +follow the CamelCase convention for class names, then simply do not +capitalize the first letter of the destination variable for the instance. @node Temporary Instances @subsection Temporary Instances Similar to @ref{Temporary Classes,}, you may wish to use an @emph{instance} -temporarily to invoke a method or chain of methods. @dfn{Temporary instances} -are instances that are instantiated in order to invoke a method or chain of -methods, then are immediately discarded. +temporarily to invoke a method or chain of methods. @dfn{Temporary +instances} are instances that are instantiated in order to invoke a method +or chain of methods, then are immediately discarded. @float Figure, f:inst-tmp @verbatim @@ -423,7 +431,11 @@ methods, then are immediately discarded. var name = Foo().getName(); // method chaining - var car = VehicleFactory().createBody().addWheel( 4 ).addDoor( 2 ).build(); + var car = VehicleFactory() + .createBody() + .addWheel( 4 ) + .addDoor( 2 ) + .build(); // temporary class with callback HttpRequest( host, port ).get( path, function( data ) @@ -431,34 +443,34 @@ methods, then are immediately discarded. console.log( data ); } ); - // Conventionally (without ease.js), you'd accomplish the above using the - // 'new' keyword. You may still do this with ease.js, though it is less - // clean looking. + // Conventionally (without ease.js), you'd accomplish the above using + // the 'new' keyword. You may still do this with ease.js, though it is + // less clean looking. ( new Foo() ).someMethod(); @end verbatim @caption{Declaring a temporary (throwaway) class} @end float Rather than storing the class instance, we are using it simply to invoke -methods. The results of those methods are stored in the variable rather than the -class instance. The instance is immediately discarded, since it is no longer -able to be referenced, and is as such a temporary instance. +methods. The results of those methods are stored in the variable rather than +the class instance. The instance is immediately discarded, since it is no +longer able to be referenced, and is as such a temporary instance. In order for method chaining to work, each method must return itself. This pattern is useful for when a class requires instantiation in order to -invoke a method. Classes that intend to be frequently used in this manner should -declare static methods so that they may be accessed without the overhead of -creating a new class instance. +invoke a method. Classes that intend to be frequently used in this manner +should declare static methods so that they may be accessed without the +overhead of creating a new class instance. @node Inheritance @section Inheritance @table @code -@item C' = Class( string @var{name} ).extend( Object @var{base}, Object @var{dfn} ) -Define named class @var{C'} identified by @var{name} as a subtype of @var{base}, -described by @var{dfn}. @var{base} may be of type @code{Class} or may be any -enumerable object. +@item C' = Class( string @var{name} ).extend( Object @var{base}, Object +@var{dfn} ) Define named class @var{C'} identified by @var{name} as a +subtype of @var{base}, described by @var{dfn}. @var{base} may be of type +@code{Class} or may be any enumerable object. @item C' = C.extend( Object @var{dfn} ) Define anonymous class @var{C'} as a subtype of class @var{C}, described by @@ -470,34 +482,35 @@ Define anonymous class @var{C'} as a subtype of @var{base}, described by object. @end table -@var{C} is a class as defined in @ref{Defining Classes}. @var{base} may be any -class or object containing enumerable members. @var{dfn} is to be a definition -object as defined in @ref{dfnobj,,Definition Object}. +@var{C} is a class as defined in @ref{Defining Classes}. @var{base} may be +any class or object containing enumerable members. @var{dfn} is to be a +definition object as defined in @ref{dfnobj,,Definition Object}. Provided non-final @var{C} or @var{base} to satisfy requirements of @var{C}, class @var{C'} will be defined as a @dfn{subtype} (child) of @dfn{supertype} (parent) class @var{C}. Provided @var{base} that does @emph{not} satisfy -requirements of @var{C}, @var{C'} will be functionally equivalent to a subtype -of anonymous class @var{B} as defined by @var{B} = Class( @var{base} ). +requirements of @var{C}, @var{C'} will be functionally equivalent to a +subtype of anonymous class @var{B} as defined by @var{B} = +Class( @var{base} ). @subsection Member Inheritance Let @var{dfn\_n\^c} denote a member of @var{dfn} in regards to class @var{c} -that matches (case-sensitive) name @var{n}. Let @var{o\_n} denote an override, -represented as boolean value that is true under the condition that both -@var{dfn\_n\^C'} and @var{dfn\_n\^C} are defined values. +that matches (case-sensitive) name @var{n}. Let @var{o\_n} denote an +override, represented as boolean value that is true under the condition that +both @var{dfn\_n\^C'} and @var{dfn\_n\^C} are defined values. @var{C'} will @dfn{inherit} all public and protected members of supertype -@var{C} such that @var{dfn\_n\^C'} = @var{dfn\_n\^C} for each @var{dfn\^C}. For -any positive condition @var{o\_n}, member @var{dfn\_n\^C'} will be said to -@dfn{override} member @var{dfn\_n\^C}, provided that overriding member @var{n} -passes all validation rules associated with the operation. A @code{protected} -member may be @dfn{escalated} to @code{public}, but the reverse is untrue. -@code{private} members are invisible to subtypes.@footnote{This is true -conceptually, but untrue in pre-ES5 environments where ease.js is forced to fall -back (@pxref{Private Member Dilemma}). As such, one should always develop in an -ES5 or later environment to ensure visibility restrictions are properly -enforced.} +@var{C} such that @var{dfn\_n\^C'} = @var{dfn\_n\^C} for each @var{dfn\^C}. +For any positive condition @var{o\_n}, member @var{dfn\_n\^C'} will be said +to @dfn{override} member @var{dfn\_n\^C}, provided that overriding member +@var{n} passes all validation rules associated with the operation. A +@code{protected} member may be @dfn{escalated} to @code{public}, but the +reverse is untrue. @code{private} members are invisible to +subtypes.@footnote{This is true conceptually, but untrue in pre-ES5 +environments where ease.js is forced to fall back (@pxref{Private Member +Dilemma}). As such, one should always develop in an ES5 or later environment +to ensure visibility restrictions are properly enforced.} For any positive condition @var{o\_n} where member @var{n} is defined as a @emph{method}: @@ -512,32 +525,32 @@ One of the following conditions must always be true: @ref{Member Keywords,,@code{override}} keyword. @itemize @item - Note that @var{dfn\_n\^C'} will not become @ref{Member Keywords,,@code{virtual}} - by default (unlike languages such as C++); they - must be explicitly declared as such. + Note that @var{dfn\_n\^C'} will not become @ref{Member + Keywords,,@code{virtual}} by default (unlike languages such as C++); + they must be explicitly declared as such. @end itemize @item - @var{dfn\_n\^C} is declared with the @ref{Member Keywords,,@code{abstract}} - keyword and @var{dfn\_n\^C'} omits the @ref{Member Keywords,,@code{override}} - keywords. + @var{dfn\_n\^C} is declared with the @ref{Member + Keywords,,@code{abstract}} keyword and @var{dfn\_n\^C'} omits the + @ref{Member Keywords,,@code{override}} keywords. @end itemize @item -The argument count of method @var{dfn\_n\^C'} must be >= the argument count of -method @var{dfn\_n\^C} to permit polymorphism. +The argument count of method @var{dfn\_n\^C'} must be >= the argument count +of method @var{dfn\_n\^C} to permit polymorphism. @item -A reference to super method @var{dfn\_n\^C} will be preserved and assigned to -@samp{this.__super} within context of method @var{dfn\_n\^C'}. +A reference to super method @var{dfn\_n\^C} will be preserved and assigned +to @samp{this.__super} within context of method @var{dfn\_n\^C'}. @item A method is said to be @dfn{concrete} when it provides a definition and @dfn{abstract} when it provides only a declaration (@pxref{dfnobj,,Definition Object}). @itemize @item - Any method @var{n} such that @var{dfn\_n\^C} is declared @code{abstract} may - be overridden by a concrete or abstract method @var{dfn\_n\^C'}. + Any method @var{n} such that @var{dfn\_n\^C} is declared @code{abstract} + may be overridden by a concrete or abstract method @var{dfn\_n\^C'}. @item - A method @var{n} may @emph{not} be declared @ref{Member Keywords,,@code{abstract}} - if @var{dfn\_n\^C} is concrete. + A method @var{n} may @emph{not} be declared @ref{Member + Keywords,,@code{abstract}} if @var{dfn\_n\^C} is concrete. @end itemize @item Member @var{dfn\_n\^C'} must be a method. @@ -550,11 +563,11 @@ Members that have been declared @code{static} cannot be overridden (@pxref{Static Members}). @subsection Discussion -Inheritance can be a touchy subject among many Object-Oriented developers due to -encapsulation concerns and design considerations over method overrides. The -decision of whether or not inheritance is an appropriate choice over composition -is left to the developer; ease.js provides the facilities for achieving -classical inheritance where it is desired. +Inheritance can be a touchy subject among many Object-Oriented developers +due to encapsulation concerns and design considerations over method +overrides. The decision of whether or not inheritance is an appropriate +choice over composition is left to the developer; ease.js provides the +facilities for achieving classical inheritance where it is desired. @float Figure, f:inheritance-ex @image{img/inheritance-ex} @@ -577,12 +590,12 @@ of the two. We describe inheritance as an ``is a'' relationship. That is: @end itemize Subtypes @dfn{inherit} all public and protected members of their supertypes -(@pxref{Access Modifiers}). This means that, in the case of our above example, -the @code{walk()} and @code{bark()} methods would be available to our subtypes. -If the subtype also defines a method of the same name, as was done above, it -will @dfn{override} the parent functionality. For now, we will limit our -discussion to public members. How would we represent these classes using -ease.js? +(@pxref{Access Modifiers}). This means that, in the case of our above +example, the @code{walk()} and @code{bark()} methods would be available to +our subtypes. If the subtype also defines a method of the same name, as was +done above, it will @dfn{override} the parent functionality. For now, we +will limit our discussion to public members. How would we represent these +classes using ease.js? @float Figure, f:inheritance @verbatim @@ -621,52 +634,53 @@ ease.js? @caption{Inheritance in ease.js} @end float -You should already understand how to define a class (@pxref{Defining Classes}). -The above example introduced two means of @dfn{extending} classes -- defining a -new class that inherits from a parent: +You should already understand how to define a class (@pxref{Defining +Classes}). The above example introduced two means of @dfn{extending} +classes -- defining a new class that inherits from a parent: @table @strong @item Named Subclasses -@var{LazyDog} is defined as a @emph{named} subclass -(@pxref{Anonymous vs. Named Classes}). This syntax requires the use of -@samp{Class( 'Name' )}. The @code{extend()} method then allows you to extend -from an existing class by passing the class reference in as the first argument. +@var{LazyDog} is defined as a @emph{named} subclass (@pxref{Anonymous vs. +Named Classes}). This syntax requires the use of @samp{Class( 'Name' )}. The +@code{extend()} method then allows you to extend from an existing class by +passing the class reference in as the first argument. @item Anonymous Subclasses -@var{TwoLeggedDog} was declared as an @emph{anonymous} subclass. The syntax for -this declaration is a bit more concise, but you forfeit the benefits of named -classes (@pxref{Anonymous vs. Named Classes}). In this case, you can simply call -the supertype's @code{extend()} method. Alternatively, you can use the -@samp{Class.extend( Base, @{@} )} syntax, as was used with the named subclass -@var{LazyDog}. +@var{TwoLeggedDog} was declared as an @emph{anonymous} subclass. The syntax +for this declaration is a bit more concise, but you forfeit the benefits of +named classes (@pxref{Anonymous vs. Named Classes}). In this case, you can +simply call the supertype's @code{extend()} method. Alternatively, you can +use the @samp{Class.extend( Base, @{@} )} syntax, as was used with the named +subclass @var{LazyDog}. @end table -You are @emph{always} recommended to use the named syntax when declaring classes -in order to provide more useful error messages. If you are willing to deal with -the less helpful error messages, feel free to use anonymous classes for their -conciseness. +You are @emph{always} recommended to use the named syntax when declaring +classes in order to provide more useful error messages. If you are willing +to deal with the less helpful error messages, feel free to use anonymous +classes for their conciseness. @menu * Understanding Member Inheritance:: How to work with inherited members * Overriding Methods:: Overriding inherited methods * Type Checks and Polymorphism:: Substituting similar classes for - one-another -* Visibility Escalation:: Increasing visibility of inherited members + one-another +* Visibility Escalation:: Increasing visibility of inherited + members * Final Classes:: Classes that cannot be inherited from @end menu @node Understanding Member Inheritance @subsection Understanding Member Inheritance -In @ref{f:inheritance}, we took a look at how to inherit from a parent class. -What does it mean when we ``inherit'' from a parent? What are we inheriting? The -answer is: the API. +In @ref{f:inheritance}, we took a look at how to inherit from a parent +class. What does it mean when we ``inherit'' from a parent? What are we +inheriting? The answer is: the API. There are two types of APIs that subtypes can inherit from their parents: @table @emph @item Public API -This is the API that is accessible to everyone using your class. It contains all -public members. We will be focusing on public members in this chapter. +This is the API that is accessible to everyone using your class. It contains +all public members. We will be focusing on public members in this chapter. @item Protected API Protected members make up a protected API, which is an API available to @@ -676,18 +690,18 @@ this untouched for now. @end table When a subtype inherits a member from its parent, it acts almost as if that -member was defined in the class itself@footnote{This statement is not to imply -that inheritance is a case of copy-and-paste. There are slight variations, which -are discussed in more detail in the Access Modifiers section (@pxref{Access -Modifiers}).}. This means that the subtype can use the inherited members as if -they were its own (keep in mind that members also include properties). This -means that we @emph{do not} have to redefine the members in order to use them -ourselves. +member was defined in the class itself@footnote{This statement is not to +imply that inheritance is a case of copy-and-paste. There are slight +variations, which are discussed in more detail in the Access Modifiers +section (@pxref{Access Modifiers}).}. This means that the subtype can use +the inherited members as if they were its own (keep in mind that members +also include properties). This means that we @emph{do not} have to redefine +the members in order to use them ourselves. @var{LazyDog} and @var{TwoLeggedDog} both inherit the @code{walk()} and -@code{bark()} methods from the @var{Dog} supertype. Using @var{LazyDog} as an -example, let's see what happens when we attempt to use the @code{bark()} method -inherited from the parent. +@code{bark()} methods from the @var{Dog} supertype. Using @var{LazyDog} as +an example, let's see what happens when we attempt to use the @code{bark()} +method inherited from the parent. @float Figure, f:using-inherited-members @verbatim @@ -711,29 +725,29 @@ inherited from the parent. @caption{Using inherited members} @end float -In @ref{f:using-inherited-members} above, we added a @code{poke()} method to our -@var{LazyDog} class. This method will call the @code{bark()} method that was -inherited from @var{Dog}. If we actually run the example, you will notice that -the dog does indeed bark, showing that we are able to call our parent's method -even though we did not define it ourselves. +In @ref{f:using-inherited-members} above, we added a @code{poke()} method to +our @var{LazyDog} class. This method will call the @code{bark()} method that +was inherited from @var{Dog}. If we actually run the example, you will +notice that the dog does indeed bark, showing that we are able to call our +parent's method even though we did not define it ourselves. @node Overriding Methods @subsection Overriding Methods -When a method is inherited, you have the option of either keeping the parent's -implementation or overriding it to provide your own. When you override a method, -you replace whatever functionality was defined by the parent. This concept was -used to make our @var{LazyDog} lazy and our @var{TwoLeggedDog} walk on two legs -in @ref{f:inheritance}. +When a method is inherited, you have the option of either keeping the +parent's implementation or overriding it to provide your own. When you +override a method, you replace whatever functionality was defined by the +parent. This concept was used to make our @var{LazyDog} lazy and our +@var{TwoLeggedDog} walk on two legs in @ref{f:inheritance}. After overriding a method, you may still want to invoke the parent's method. This allows you to @emph{augment} the functionality rather than replacing it entirely. ease.js provides a magic @code{__super()} method to do this. This -method is defined only for the overriding methods and calls the parent method -that was overridden. +method is defined only for the overriding methods and calls the parent +method that was overridden. -In order to demonstrate this, let's add an additional subtype to our hierarchy. -@var{AngryDog} will be a subtype of @var{LazyDog}. Not only is this dog lazy, -but he's rather moody. +In order to demonstrate this, let's add an additional subtype to our +hierarchy. @var{AngryDog} will be a subtype of @var{LazyDog}. Not only is +this dog lazy, but he's rather moody. @float Figure, f:super-method @verbatim @@ -759,30 +773,31 @@ but he's rather moody. @caption{Using @code{__super()} method} @end float -If you remember from @ref{f:using-inherited-members}, we added a @code{poke()} -method to @var{LazyDog}. In @ref{f:super-method} above, we are overriding this -method so that @var{AngryDog} growls when you poke him. However, we still want -to invoke @var{LazyDog}'s default behavior when he's poked, so we also call the -@code{__super()} method. This will also make @var{AngryDog} bark like -@var{LazyDog}. +If you remember from @ref{f:using-inherited-members}, we added a +@code{poke()} method to @var{LazyDog}. In @ref{f:super-method} above, we are +overriding this method so that @var{AngryDog} growls when you poke him. +However, we still want to invoke @var{LazyDog}'s default behavior when he's +poked, so we also call the @code{__super()} method. This will also make +@var{AngryDog} bark like @var{LazyDog}. It is important to note that @code{__super()} must be invoked like any other -method. That is, if the overridden method requires arguments, you must pass them -to @code{__super()}. This allows you to modify the argument list before it is -sent to the overridden method. +method. That is, if the overridden method requires arguments, you must pass +them to @code{__super()}. This allows you to modify the argument list before +it is sent to the overridden method. @node Type Checks and Polymorphism @subsection Type Checks and Polymorphism -The fact that the API of the parent is inherited is a very important detail. If -the API of subtypes is guaranteed to be @emph{at least} that of the parent, then -this means that a function expecting a certain type can also work with any -subtypes. This concept is referred to as @dfn{polymorphism}, and is a very -powerful aspect of Object-Oriented programming. +The fact that the API of the parent is inherited is a very important detail. +If the API of subtypes is guaranteed to be @emph{at least} that of the +parent, then this means that a function expecting a certain type can also +work with any subtypes. This concept is referred to as @dfn{polymorphism}, +and is a very powerful aspect of Object-Oriented programming. -Let's consider a dog trainer. A dog trainer can generally train any type of dog -(technicalities aside), so it would stand to reason that we would want our dog -trainer to be able to train @var{LazyDog}, @var{AngryDog}, @var{TwoLeggedDog}, -or any other type of @var{Dog} that we may throw at him/her. +Let's consider a dog trainer. A dog trainer can generally train any type of +dog (technicalities aside), so it would stand to reason that we would want +our dog trainer to be able to train @var{LazyDog}, @var{AngryDog}, +@var{TwoLeggedDog}, or any other type of @var{Dog} that we may throw at +him/her. @float Figure, f:polymorphism-uml @image{img/composition-uml} @@ -791,18 +806,18 @@ or any other type of @var{Dog} that we may throw at him/her. Type checks are traditionally performed in JavaScript using the @code{instanceOf} operator. While this can be used in most inheritance cases -with ease.js, it is not recommended. Rather, you are encouraged to use ease.js's -own methods for determining instance type@footnote{The reason for this will -become clear in future chapters. ease.js's own methods permit checking for -additional types, such as Interfaces.}. Support for the @code{instanceOf} -operator is not guaranteed. +with ease.js, it is not recommended. Rather, you are encouraged to use +ease.js's own methods for determining instance type@footnote{The reason for +this will become clear in future chapters. ease.js's own methods permit +checking for additional types, such as Interfaces.}. Support for the +@code{instanceOf} operator is not guaranteed. Instead, you have two choices with ease.js: @table @code @item Class.isInstanceOf( type, instance ); -Returns @code{true} if @var{instance} is of type @var{type}. Otherwise, returns -@code{false}. +Returns @code{true} if @var{instance} is of type @var{type}. Otherwise, +returns @code{false}. @item Class.isA( type, instance ); Alias for @code{Class.isInstanceOf()}. Permits code that may read better @@ -830,12 +845,13 @@ For example: @caption{Using ease.js to determine instance type} @end float -It is important to note that, as demonstrated in @ref{f:instanceof-ex} above, an -@emph{instance} must be passed as a second argument, not a class. +It is important to note that, as demonstrated in @ref{f:instanceof-ex} +above, an @emph{instance} must be passed as a second argument, not a class. -Using this method, we can ensure that the @var{DogTrainer} may only be used with -an instance of @var{Dog}. It doesn't matter what instance of @var{Dog} - be it a -@var{LazyDog} or otherwise. All that matters is that we are given a @var{Dog}. +Using this method, we can ensure that the @var{DogTrainer} may only be used +with an instance of @var{Dog}. It doesn't matter what instance of @var{Dog} +- be it a @var{LazyDog} or otherwise. All that matters is that we are given +a @var{Dog}. @float Figure, f:polymorphism-easejs @verbatim @@ -866,25 +882,25 @@ an instance of @var{Dog}. It doesn't matter what instance of @var{Dog} - be it a @caption{Polymorphism in ease.js} @end float -It is very important that you use @emph{only} the API of the type that you are -expecting. For example, only @var{LazyDog} and @var{AngryDog} implement a -@code{poke()} method. It is @emph{not} a part of @var{Dog}'s API. Therefore, it -should not be used in the @var{DogTrainer} class. Instead, if you wished to use -the @code{poke()} method, you should require that an instance of @var{LazyDog} -be passed in, which would also permit @var{AngryDog} (since it is a subtype of -@var{LazyDog}). +It is very important that you use @emph{only} the API of the type that you +are expecting. For example, only @var{LazyDog} and @var{AngryDog} implement +a @code{poke()} method. It is @emph{not} a part of @var{Dog}'s API. +Therefore, it should not be used in the @var{DogTrainer} class. Instead, if +you wished to use the @code{poke()} method, you should require that an +instance of @var{LazyDog} be passed in, which would also permit +@var{AngryDog} (since it is a subtype of @var{LazyDog}). Currently, it is necessary to perform this type check yourself. In future -versions, ease.js will allow for argument type hinting/strict typing, which will -automate this check for you. +versions, ease.js will allow for argument type hinting/strict typing, which +will automate this check for you. @node Visibility Escalation @subsection Visibility Escalation Let @var{a\_n} denote a numeric level of visibility for @var{dfn\_n\^C} such that the access modifiers (@pxref{Access Modifiers}) @code{private}, @code{protected} and @code{public} are associated with the values @code{1}, -@code{2} and @code{3} respectively. Let @var{a'} represent @var{a} in regards to -@var{C'} (@pxref{Inheritance}). +@code{2} and @code{3} respectively. Let @var{a'} represent @var{a} in +regards to @var{C'} (@pxref{Inheritance}). For any member @var{n} of @var{dfn}, the following must be true: @itemize @@ -896,29 +912,30 @@ For any member @var{n} of @var{dfn}, the following must be true: @end itemize @subsubsection Discussion -@dfn{Visibility escalation} is the act of increasing the visibility of a member. -Since private members cannot be inherited, this would then imply that the only -act to be considered "escallation" would be increasing the level of visibility -from @code{protected} to @code{private}. +@dfn{Visibility escalation} is the act of increasing the visibility of a +member. Since private members cannot be inherited, this would then imply +that the only act to be considered "escallation" would be increasing the +level of visibility from @code{protected} to @code{private}. -Many follow the convention of prefixing private members with an underscore but -leaving omitting such a prefix from protected members. This is to permit -visibility escalation without renaming the member. Alternatively, a new member -can be defined without the prefix that will simply call the overridden member -(although this would then not be considered an escalation, since the member name -varies). +Many follow the convention of prefixing private members with an underscore +but leaving omitting such a prefix from protected members. This is to permit +visibility escalation without renaming the member. Alternatively, a new +member can be defined without the prefix that will simply call the +overridden member (although this would then not be considered an escalation, +since the member name varies). -In order to increase the visibility, you must override the member; you cannot -simply redeclare it, leaving the parent definition in tact. For properties, this -has no discernible effect unless the @var{value} changes, as you are simply -redefining it. For methods, this means that you are overriding the entire -@var{value}. Therefore, you will either have to provide an alternate -implementation or call @samp{this.__super()} to invoke the original method. +In order to increase the visibility, you must override the member; you +cannot simply redeclare it, leaving the parent definition in tact. For +properties, this has no discernible effect unless the @var{value} changes, +as you are simply redefining it. For methods, this means that you are +overriding the entire @var{value}. Therefore, you will either have to +provide an alternate implementation or call @samp{this.__super()} to invoke +the original method. Note that @emph{you cannot de-escalate from public to protected}; this will -result in an error. This ensures that once a class defines an API, subclasses -cannot alter it. That API must be forever for all subtypes to ensure that it -remains polymorphic. +result in an error. This ensures that once a class defines an API, +subclasses cannot alter it. That API must be forever for all subtypes to +ensure that it remains polymorphic. Let's take a look at an example. @@ -986,24 +1003,24 @@ Classes}), with the exception that they cannot be inherited from. @node Static Members @section Static Members @dfn{Static members} do not require instantiation of the containing class in -order to be used, but may also be called by instances. They are attached to the -class itself rather than an instance. Static members provide convenience under -certain circumstances where class instantiation is unnecessary and permit -sharing data between instances of a class. However, static members, when used -improperly, can produce poorly designed classes and tightly coupled code that is -also difficult to test. Static properties also introduce problems very similar -to global variables. +order to be used, but may also be called by instances. They are attached to +the class itself rather than an instance. Static members provide convenience +under certain circumstances where class instantiation is unnecessary and +permit sharing data between instances of a class. However, static members, +when used improperly, can produce poorly designed classes and tightly +coupled code that is also difficult to test. Static properties also +introduce problems very similar to global variables. -Let us consider an implementation of the factory pattern. Class @var{BigBang} -will declare two static methods in order to satisfy different means of -instantiation: @code{fromBraneCollision()} and @code{fromBigCrunch()} (for the -sake of the example, we're not going to address every theory). Let us also -consider that we want to keep track of the number of big bangs in our universe -(perhaps to study whether or not a "Big Crunch" could have potentially -happened in the past) by incrementing a counter each time a new big bang occurs. -Because we are using a static method, we cannot use a property of an instance in -order to store this data. Therefore, we will use a static property of class -@var{BigBang}. +Let us consider an implementation of the factory pattern. Class +@var{BigBang} will declare two static methods in order to satisfy different +means of instantiation: @code{fromBraneCollision()} and +@code{fromBigCrunch()} (for the sake of the example, we're not going to +address every theory). Let us also consider that we want to keep track of +the number of big bangs in our universe (perhaps to study whether or not a +"Big Crunch" could have potentially happened in the past) by incrementing a +counter each time a new big bang occurs. Because we are using a static +method, we cannot use a property of an instance in order to store this data. +Therefore, we will use a static property of class @var{BigBang}. @float Figure, f:static-ex @verbatim @@ -1095,7 +1112,8 @@ order to store this data. Therefore, we will use a static property of class @end float Due to limitations of pre-ECMAScript 5 implementations, ease.js's static -implementation must be broken into two separate parts: properties and methods. +implementation must be broken into two separate parts: properties and +methods. @menu * Static Methods:: @@ -1105,18 +1123,19 @@ implementation must be broken into two separate parts: properties and methods. @node Static Methods @subsection Static Methods -In @ref{f:static-ex}, we implemented three static methods: two factory methods, -@code{fromBraneCollision()} and @code{FromBigCrunch()}, and one getter method to -retrieve the total number of big bangs, @code{getTotalCount()}. These methods are -very similar to instance methods we are already used to, with a few important -differences: +In @ref{f:static-ex}, we implemented three static methods: two factory +methods, @code{fromBraneCollision()} and @code{FromBigCrunch()}, and one +getter method to retrieve the total number of big bangs, +@code{getTotalCount()}. These methods are very similar to instance methods +we are already used to, with a few important differences: @enumerate @item Static methods are declared with the @code{static} keyword. @item -In the body, @code{this} is bound to the class itself, rather than the instance. +In the body, @code{this} is bound to the class itself, rather than the +instance. @item Static methods cannot call any non-static methods of the same class without @@ -1124,10 +1143,10 @@ first instantiating it. @end enumerate The final rule above is not true when the situation is reversed. Non-static -methods @emph{can} call static methods through use of the @var{__self} object, -which is a reference to the class itself. That is, @var{this} in a static method -is the same object as @var{this.__self} in a non-static method. This is -demonstrated by @code{getTotalCount()} +methods @emph{can} call static methods through use of the @var{__self} +object, which is a reference to the class itself. That is, @var{this} in a +static method is the same object as @var{this.__self} in a non-static +method. This is demonstrated by @code{getTotalCount()} @verbatim this.$('_count') @@ -1140,24 +1159,24 @@ and @code{__construct()}. @end verbatim To help remember @var{__self}, consider what the name states. A class is a -definition used to create an object. The body of a method is a definition, which -is defined on the class. Therefore, even though the body of a method may be -called in the context of an instance, it is still part of the class. As such, -@var{__self} refers to the class. +definition used to create an object. The body of a method is a definition, +which is defined on the class. Therefore, even though the body of a method +may be called in the context of an instance, it is still part of the class. +As such, @var{__self} refers to the class. @node Static Properties @subsection Static Properties You have likely noticed by now that static properties are handled a bit -differently than both static methods and non-static properties. This difference -is due to pre-ECMAScript 5 limitations and is discussed at length in the -@ref{Static Implementation} section. +differently than both static methods and non-static properties. This +difference is due to pre-ECMAScript 5 limitations and is discussed at length +in the @ref{Static Implementation} section. -Static properties are read from and written to using the @dfn{static accessor -method} @code{$()}. This method name was chosen because the @code{$} prefix is -common in scripting languages such as BASH, Perl (for scalars) and PHP. The -accessor method accepts two arguments, the second being optional. If only the -first argument is provided, the accessor method acts as a getter, as in -@ref{f:static-ex}'s @code{getTotalCount()}: +Static properties are read from and written to using the @dfn{static +accessor method} @code{$()}. This method name was chosen because the +@code{$} prefix is common in scripting languages such as BASH, Perl (for +scalars) and PHP. The accessor method accepts two arguments, the second +being optional. If only the first argument is provided, the accessor method +acts as a getter, as in @ref{f:static-ex}'s @code{getTotalCount()}: @verbatim return this.$('_count'); @@ -1172,11 +1191,11 @@ If the second argument is provided, it acts as a setter, as in ); @end verbatim -Setting @code{undefined} values is supported. The @code{delete} operator is not -supported, as its use is both restricted by the language itself and doesn't make -sense to use in this context. As hinted by the example above, the increment and -decrement operators (@code{++} and @code{--}) are not supported because -JavaScript does not permit returning values by reference. +Setting @code{undefined} values is supported. The @code{delete} operator is +not supported, as its use is both restricted by the language itself and +doesn't make sense to use in this context. As hinted by the example above, +the increment and decrement operators (@code{++} and @code{--}) are not +supported because JavaScript does not permit returning values by reference. It is important to understand that, currently, the accessor method cannot be omitted. Consider the following example: @@ -1210,19 +1229,20 @@ omitted. Consider the following example: @subsection Constants @dfn{Constants}, in terms of classes, are immutable static properties. This means that, once defined, a constant cannot be modified. Since the value is -immutable, it does not make sense to create instances of the property. As such, -constant values are implicitly static. This ensures that each instance, as well -as any static access, references the exact same value. This is especially -important for objects and arrays. +immutable, it does not make sense to create instances of the property. As +such, constant values are implicitly static. This ensures that each +instance, as well as any static access, references the exact same value. +This is especially important for objects and arrays. -One important difference between other languages, such as PHP, is that ease.js -supports the @ref{Access Modifiers, visibility modifiers} in conjunction with -the @code{const} keyword. That is, you can have public, protected and private -constants. Constants are public by default, like every other type of member. -This feature permits encapsulating constant values, which is important if you -want an immutable value that shouldn't be exposed to the rest of the world (e.g. -a service URL, file path, etc). Consider the following example in which we have -a class responsible for reading mount mounts from @file{/etc/fstab}: +One important difference between other languages, such as PHP, is that +ease.js supports the @ref{Access Modifiers, visibility modifiers} in +conjunction with the @code{const} keyword. That is, you can have public, +protected and private constants. Constants are public by default, like every +other type of member. This feature permits encapsulating constant values, +which is important if you want an immutable value that shouldn't be exposed +to the rest of the world (e.g. a service URL, file path, etc). Consider the +following example in which we have a class responsible for reading mount +mounts from @file{/etc/fstab}: @float Figure, f:const-ex @verbatim @@ -1245,24 +1265,24 @@ a class responsible for reading mount mounts from @file{/etc/fstab}: @caption{Using the @code{const} keyword} @end float -In the above example, attempting to access the @var{_PATH} constant from outside -the class would return @code{undefined}. Had the constant been declared as -public, or had the visibility modifier omitted, it could have been accessed just -like any other static property: +In the above example, attempting to access the @var{_PATH} constant from +outside the class would return @code{undefined}. Had the constant been +declared as public, or had the visibility modifier omitted, it could have +been accessed just like any other static property: @verbatim // if PATH were a public constant value MountPointIterator.$('PATH'); @end verbatim -Any attempts to modify the value of a constant will result in an exception. This -will also work in pre-ES5 engines due to use of the @ref{Static Properties, -static accessor method} (@code{$()}). +Any attempts to modify the value of a constant will result in an exception. +This will also work in pre-ES5 engines due to use of the @ref{Static +Properties, static accessor method} (@code{$()}). -It is important to note that constants prevent the @emph{value of the property} -from being reassigned. It @emph{does not} prevent modification of the value that -is @emph{referenced} by the property. For example, if we had a constant -@var{foo}, which references an object, such that +It is important to note that constants prevent the @emph{value of the +property} from being reassigned. It @emph{does not} prevent modification of +the value that is @emph{referenced} by the property. For example, if we had +a constant @var{foo}, which references an object, such that @verbatim 'const foo': { a: 'b' } @@ -1279,8 +1299,8 @@ it is perfectly legal to alter the object: @section Abstract Members @table @code @item 'abstract [@var{keywords}] @var{name}': @var{params} -Declare an abstract method @var{name} as having @var{params} parameters, having -optional additional keywords +Declare an abstract method @var{name} as having @var{params} parameters, +having optional additional keywords @var{keywords}. @end table Abstract members permit declaring an API, deferring the implementation to a @@ -1288,8 +1308,8 @@ subtype. Abstract methods are declared as an array of string parameter names @var{params}. @verbatim - // declares abstract method 'connect' expecting the two parameters, 'host' - // and 'path' + // declares abstract method 'connect' expecting the two parameters, + // 'host' and 'path' { 'abstract connect': [ 'host', 'path' ] } @end verbatim @@ -1309,8 +1329,8 @@ The subtype must implement at least the number of parameters declared in @var{params}, but the names needn't match. @itemize @item - Each name in @var{params} must be a valid variable name, as satisfied by the - regular expression @code{/^[a-z_][a-z0-9_]*$/i}. + Each name in @var{params} must be a valid variable name, as satisfied by + the regular expression @code{/^[a-z_][a-z0-9_]*$/i}. @item The names are use purely for documentation and are not semantic. @end itemize @@ -1327,10 +1347,12 @@ Abstract members may only be a part of one of the following: @subsection Interfaces @table @code @item I = Interface( string @var{name}, Object @var{dfn} ) -Define named interface @var{I} identified by @var{name} described by @var{dfn}. +Define named interface @var{I} identified by @var{name} described by +@var{dfn}. @item I = Interface( string @var{name} ).extend( Object @var{dfn} ) -Define named interface @var{I} identified by @var{name} described by @var{dfn}. +Define named interface @var{I} identified by @var{name} described by +@var{dfn}. @item I = Interface( Object @var{dfn} ) Define anonymous interface @var{I} as described by @var{dfn}. @@ -1361,12 +1383,12 @@ Interfaces may only extend other interfaces (@pxref{Inheritance}). @subsubsection Implementing Interfaces @table @code -@item C = Class( @var{name} ).implement( @var{I\_0}[, ...@var{I\_n}] ).extend( @var{dfn} ) -Define named class @var{C} identified by @var{name} implementing all interfaces -@var{I}, described by @var{dfn}. +@item C = Class( @var{name} ).implement( @var{I\_0}[, ...@var{I\_n}] +).extend( @var{dfn} ) Define named class @var{C} identified by @var{name} +implementing all interfaces @var{I}, described by @var{dfn}. @item C = Class.implement( @var{I\_0}[, ...@var{I\_n} ).extend( @var{dfn} ) -Define anonymous class @var{C} implementing all interfaces @var{I}, described -by @var{dfn}. +Define anonymous class @var{C} implementing all interfaces @var{I}, +described by @var{dfn}. @end table Any class @var{C} may implement any interface @var{I}, inheriting its API. Unlike class inheritance, any class @var{C} may implement one or more @@ -1374,34 +1396,36 @@ interfaces. @itemize @item -Class @var{C} implementing interfaces @var{I} will be considered a subtype of -every @var{I}. +Class @var{C} implementing interfaces @var{I} will be considered a subtype +of every @var{I}. @item Class @var{C} must either: @itemize - @item Provide a concrete definition for every member of @var{dfn} of @var{I}, + @item Provide a concrete definition for every member of @var{dfn} of + @var{I}, @item or be declared as an @code{AbstractClass} (@pxref{Abstract Classes}) @itemize @item - @var{C} may be declared as an @code{AbstractClass} while still providing a - concrete definition for some of @var{dfn} of @var{I}. + @var{C} may be declared as an @code{AbstractClass} while still providing + a concrete definition for some of @var{dfn} of @var{I}. @end itemize @end itemize @end itemize @subsubsection Discussion -Consider a library that provides a websocket abstraction. Not all environments -support web sockets, so an implementation may need to fall back on long polling -via AJAX, Flash sockets, etc. If websocket support @emph{is} available, one -would want to use that. Furthermore, an environment may provide its own type of -socket that our library does not include support for. Therefore, we would want -to provide developers for that environment the ability to define their own type -of socket implementation to be used in our library. +Consider a library that provides a websocket abstraction. Not all +environments support web sockets, so an implementation may need to fall back +on long polling via AJAX, Flash sockets, etc. If websocket support @emph{is} +available, one would want to use that. Furthermore, an environment may +provide its own type of socket that our library does not include support +for. Therefore, we would want to provide developers for that environment the +ability to define their own type of socket implementation to be used in our +library. -This type of abstraction can be solved simply by providing a generic API that -any operation on websockets may use. For example, this API may provide -@code{connect()}, @code{onReceive()} and @code{send()} operations, among others. We -could define this API in a @code{Socket} interface: +This type of abstraction can be solved simply by providing a generic API +that any operation on websockets may use. For example, this API may provide +@code{connect()}, @code{onReceive()} and @code{send()} operations, among +others. We could define this API in a @code{Socket} interface: @float Figure, f:interface-def @verbatim @@ -1441,7 +1465,8 @@ var WebSocket = Class( 'WebSocket' ).implement( Socket ).extend( @caption{Implementing an interface} @end float -Anything wishing to use sockets can work with this interface polymorphically: +Anything wishing to use sockets can work with this interface +polymorphically: @float Figure, f:interface-poly @verbatim @@ -1478,22 +1503,25 @@ implementations: @float Figure, f:interface-poly-use @verbatim ChatClient( WebSocket() ).sendMessage( '#lobby', "Sweet! WebSockets!" ); - ChatClient( SomeCustomSocket() ).sendMessage( '#lobby', "I can chat too!" ); + ChatClient( SomeCustomSocket() ) + .sendMessage( '#lobby', "I can chat too!" ); @end verbatim @caption{Obtaining flexibility via dependency injection} @end float The use of the @code{Socket} interface allowed us to create a powerful -abstraction that will allow our library to work across any range of systems. The -use of an interface allows us to define a common API through which all of our -various components may interact without having to worry about the implementation -details - something we couldn't worry about even if we tried, due to the fact -that we want developers to support whatever environment they are developing for. +abstraction that will allow our library to work across any range of systems. +The use of an interface allows us to define a common API through which all +of our various components may interact without having to worry about the +implementation details - something we couldn't worry about even if we tried, +due to the fact that we want developers to support whatever environment they +are developing for. Let's make a further consideration. Above, we defined a @code{onReceive()} -method which accepts a callback to be called when data is received. What if our -library wished to use an @code{Event} interface as well, which would allow us to -do something like @samp{some_socket.on( 'receive', function() @{@} )}? +method which accepts a callback to be called when data is received. What if +our library wished to use an @code{Event} interface as well, which would +allow us to do something like @samp{some_socket.on( 'receive', function() +@{@} )}? @float Figure, f:interface-impl-multi @verbatim @@ -1508,34 +1536,36 @@ var AnotherSocket = Class.implement( Socket, Event ).extend( @end float Any class may implement any number of interfaces. In the above example, -@code{AnotherSocket} implemented both @code{Socket} and @code{Event}, allowing -it to be used wherever either type is expected. Let's take a look: +@code{AnotherSocket} implemented both @code{Socket} and @code{Event}, +allowing it to be used wherever either type is expected. Let's take a look: @float Figure, f:interface-multi-isa @verbatim Class.isA( Socket, AnotherSocket() ); // true Class.isA( Event, AnotherSocket() ); // true @end verbatim -@caption{Implementors of interfaces are considered subtypes of each implemented -interface} +@caption{Implementors of interfaces are considered subtypes of each +implemented interface} @end float -Interfaces do not suffer from the same problems as multiple inheritance, because -we are not providing any sort of implementation that may cause conflicts. +Interfaces do not suffer from the same problems as multiple inheritance, +because we are not providing any sort of implementation that may cause +conflicts. -One might then ask - why interfaces instead of abstract classes (@pxref{Abstract -Classes})? Abstract classes require subclassing, which tightly couples the -subtype with its parent. One may also only inherit from a single supertype -(@pxref{Inheritance}), which may cause a problem in our library if we used an -abstract class for @code{Socket}, but a developer had to inherit from another -class and still have that subtype act as a @code{Socket}. +One might then ask - why interfaces instead of abstract classes +(@pxref{Abstract Classes})? Abstract classes require subclassing, which +tightly couples the subtype with its parent. One may also only inherit from +a single supertype (@pxref{Inheritance}), which may cause a problem in our +library if we used an abstract class for @code{Socket}, but a developer had +to inherit from another class and still have that subtype act as a +@code{Socket}. Interfaces have no such problem. Implementors are free to use interfaces -wherever they wish and use as many as they wish; they needn't worry that they -may be unable to use the interface due to inheritance or coupling issues. -However, although interfaces facilitate API reuse, they do not aid in code -reuse as abstract classes do@footnote{This is a problem that will eventually be -solved by the introduction of traits/mixins.}. +wherever they wish and use as many as they wish; they needn't worry that +they may be unable to use the interface due to inheritance or coupling +issues. However, although interfaces facilitate API reuse, they do not aid +in code reuse as abstract classes do@footnote{This is a problem that will +eventually be solved by the introduction of traits/mixins.}. @node Abstract Classes @subsection Abstract Classes @@ -1555,9 +1585,9 @@ Define anonymous abstract class @var{A} as described by @var{dfn}. Define anonymous abstract class @var{A} as described by @var{dfn}. @end table -Abstract classes are defined with a syntax much like classes (@pxref{Defining -Classes}). They act just as classes do, except with the following additional -properties: +Abstract classes are defined with a syntax much like classes +(@pxref{Defining Classes}). They act just as classes do, except with the +following additional properties: @itemize @item @@ -1569,36 +1599,38 @@ explicitly declared as @ref{t:keywords,,@code{abstract}}. Abstract classes may extend both concrete and abstract classes @end itemize -An abstract class @emph{must} be used if any member of @var{dfn} is declared as -abstract. This serves as a form of self-documenting code, as it would otherwise -not be immediately clear whether or not a class was abstract (one would have to -look through every member of @var{dfn} to make that determination). +An abstract class @emph{must} be used if any member of @var{dfn} is declared +as abstract. This serves as a form of self-documenting code, as it would +otherwise not be immediately clear whether or not a class was abstract (one +would have to look through every member of @var{dfn} to make that +determination). @code{AbstractClass} must be imported (@pxref{Including}) from @code{easejs.AbstractClass}; it is not available in the global scope. @subsubsection Discussion -Abstract classes allow the partial implementation of an API, deferring portions -of the implementation to subtypes (@pxref{Inheritance}). As an example, let's -consider an implementation of the @dfn{Abstract Factory} pattern@footnote{See -Abstract Factory, GoF}) which is responsible for the instantiation and -initialization of an object without knowing its concrete type. +Abstract classes allow the partial implementation of an API, deferring +portions of the implementation to subtypes (@pxref{Inheritance}). As an +example, let's consider an implementation of the @dfn{Abstract Factory} +pattern@footnote{See Abstract Factory, GoF}) which is responsible for the +instantiation and initialization of an object without knowing its concrete +type. -Our hypothetical library will be a widget abstraction. For this example, let us -consider that we need a system that will work with any number of frameworks, -including jQuery UI, Dojo, YUI and others. A particular dialog needs to render a -simple @code{Button} widget so that the user may click "OK" when they have -finished reading. We cannot instantiate the widget from within the dialog -itself, as that would tightly couple the chosen widget subsystem (jQuery UI, -etc) to the dialog, preventing us from changing it in the future. Alternatively, -we could have something akin to a switch statement in order to choose which type -of widget to instantiate, but that would drastically inflate maintenance costs -should we ever need to add or remove support for other widget system in the -future. +Our hypothetical library will be a widget abstraction. For this example, let +us consider that we need a system that will work with any number of +frameworks, including jQuery UI, Dojo, YUI and others. A particular dialog +needs to render a simple @code{Button} widget so that the user may click +"OK" when they have finished reading. We cannot instantiate the widget from +within the dialog itself, as that would tightly couple the chosen widget +subsystem (jQuery UI, etc) to the dialog, preventing us from changing it in +the future. Alternatively, we could have something akin to a switch +statement in order to choose which type of widget to instantiate, but that +would drastically inflate maintenance costs should we ever need to add or +remove support for other widget system in the future. -We can solve this problem by allowing another object, a @code{WidgetFactory}, to -perform that instantiation for us. The dialog could accept the factory in its -constructor, like so: +We can solve this problem by allowing another object, a +@code{WidgetFactory}, to perform that instantiation for us. The dialog could +accept the factory in its constructor, like so: @float Figure, f:abstract-factory-use @verbatim @@ -1628,20 +1660,22 @@ Class( 'Dialog', @caption{Hypothetical use case for our Abstract Factory} @end float -We now have some other important considerations. As was previously mentioned, -@code{Dialog} itself could have determined which widget to instantiate. By using -a factory instead, we are moving that logic to the factory, but we are now -presented with a similar issue. If we use something like a switch statement to -decide what class should be instantiated, we are stuck with modifying the -factory each and every time we add or remove support for another widget library. +We now have some other important considerations. As was previously +mentioned, @code{Dialog} itself could have determined which widget to +instantiate. By using a factory instead, we are moving that logic to the +factory, but we are now presented with a similar issue. If we use something +like a switch statement to decide what class should be instantiated, we are +stuck with modifying the factory each and every time we add or remove +support for another widget library. This is where an abstract class could be of some benefit. Let's consider the -above call to @code{createButtonWidget()}, which accepted two arguments: an id -for the generated DOM element and a label for the button. Clearly, there is some -common initialization logic that can occur between each of the widgets. However, -we do not want to muddy the factory up with log to determine what widget can be -instantiated. The solution is to define the common logic, but defer the actual -instantiation of the @code{Widget} to subtypes: +above call to @code{createButtonWidget()}, which accepted two arguments: an +id for the generated DOM element and a label for the button. Clearly, there +is some common initialization logic that can occur between each of the +widgets. However, we do not want to muddy the factory up with log to +determine what widget can be instantiated. The solution is to define the +common logic, but defer the actual instantiation of the @code{Widget} to +subtypes: @float Figure, f:abstract-factory-define @verbatim @@ -1649,8 +1683,8 @@ AbstractClass( 'WidgetFactory', { 'public createButtonWidget': function( id, label ) { - // note that this is a call to an abstract method; the implementation is - // not yet defined + // note that this is a call to an abstract method; the + // implementation is not yet defined var widget = this.getNewButtonWidget(); // perform common initialization tasks @@ -1670,23 +1704,23 @@ AbstractClass( 'WidgetFactory', @end float As demonstrated in @ref{f:abstract-factory-define} above, we can see a very -interesting aspect of abstract classes: we are making a call to a method that is -not yet defined (@code{getNewButtonWidget()}@footnote{Note that we declared this -method as @ref{t:keywords,,@code{protected}} in order to encapsulate which -the widget creation logic (@pxref{Access Modifiers Discussion}). Users of the -class should not be concerned with how we accomplish our job. Indeed, they -should be concerned only with the fact that we save them the trouble of -determining which classes need to be instantiated by providing them with a -convenient API.}). Instead, by declaring it -@ref{t:keywords,,@code{abstract}}, we are stating that we want to call this -method, but it is up to a subtype to actually define it. It is for this reason -that abstract classes cannot be instantiated - they cannot be used until each of -the abstract methods have a defined implementation. +interesting aspect of abstract classes: we are making a call to a method +that is not yet defined (@code{getNewButtonWidget()}@footnote{Note that we +declared this method as @ref{t:keywords,,@code{protected}} in order to +encapsulate which the widget creation logic (@pxref{Access Modifiers +Discussion}). Users of the class should not be concerned with how we +accomplish our job. Indeed, they should be concerned only with the fact that +we save them the trouble of determining which classes need to be +instantiated by providing them with a convenient API.}). Instead, by +declaring it @ref{t:keywords,,@code{abstract}}, we are stating that we want +to call this method, but it is up to a subtype to actually define it. It is +for this reason that abstract classes cannot be instantiated - they cannot +be used until each of the abstract methods have a defined implementation. -We can now define a concrete widget factory (@pxref{Inheritance}) for each of -the available widget libraries@footnote{Of course, the @code{Widget} itself -would be its own abstraction, which may be best accomplished by the Adapter -pattern.}: +We can now define a concrete widget factory (@pxref{Inheritance}) for each +of the available widget libraries@footnote{Of course, the @code{Widget} +itself would be its own abstraction, which may be best accomplished by the +Adapter pattern.}: @float Figure, f:concrete-abstract-factory @verbatim @@ -1716,7 +1750,8 @@ statement, we opted for a polymorphic solution: @float Figure, f:abstract-factory-inject @verbatim - // we can use whatever widget library we wish by injecting it into Dialog + // we can use whatever widget library we wish by injecting it into + // Dialog Dialog( JqueryUiWidgetFactory() ).show(); Dialog( DojoWidgetFactory() ).show(); Dialog( YuiWidgetFactory() ).show(); @@ -1728,12 +1763,13 @@ injection} Now, adding or removing libraries is as simple as defining or removing a @code{WidgetFactory} class. -Another noteworthy mention is that this solution could have just as easily used -an interface instead of an abstract class (@pxref{Interfaces}). The reason we -opted for an abstract class in this scenario is due to code reuse (the common -initialization code), but in doing so, we have tightly coupled each subtype with -the supertype @code{WidgetFactory}. There are a number of trade-offs with each -implementation; choose the one that best fits your particular problem. +Another noteworthy mention is that this solution could have just as easily +used an interface instead of an abstract class (@pxref{Interfaces}). The +reason we opted for an abstract class in this scenario is due to code reuse +(the common initialization code), but in doing so, we have tightly coupled +each subtype with the supertype @code{WidgetFactory}. There are a number of +trade-offs with each implementation; choose the one that best fits your +particular problem. @node Method Proxies diff --git a/doc/easejs.css b/doc/easejs.css index ad52985..bf6d5ef 100644 --- a/doc/easejs.css +++ b/doc/easejs.css @@ -27,7 +27,7 @@ body, .float-caption } .float pre, var { - font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Courier, monospace; + font-family: 'DejaVu Sans Mono', 'Droid Sans Mono', Courier, monospace; } table { diff --git a/doc/easejs.texi b/doc/easejs.texi index 0435355..5019c3a 100644 --- a/doc/easejs.texi +++ b/doc/easejs.texi @@ -2,11 +2,11 @@ @c This document is part of the GNU ease.js manual. @c Copyright (C) 2011, 2012, 2013 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''. +@c under the terms of the GNU Free Documentation License, Version 1.3 or +@c any later version published by the Free Software Foundation; with no +@c Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. +@c A copy of the license is included in the section entitled ``GNU Free +@c Documentation License''. @c %**start of header @setfilename easejs.info diff --git a/doc/img/visobj-collection-wide.txt b/doc/img/visobj-collection-wide.txt index fe4df33..cd352fc 100644 --- a/doc/img/visobj-collection-wide.txt +++ b/doc/img/visobj-collection-wide.txt @@ -1,18 +1,18 @@ -,---------------------------------------, ,-----------------------------------, -| Per Instance of C\_0 | | Per Instance of C\_1 | -| | | | -| ,---------, ,---------, ,---------, | | ,---------, ,---------, | -| | V\_0\^0 | | V\_0\^1 | | V\_0\^2 | | | | V\_1\^0 | | V\_1\^1 | | -| `---------` `---------` `---------` | | `---------` `---------` | -| | | | | | | | | -| | ,-------, | | | | ,-------, | | -| `--------| P\_0 |--------` | | `------| P\_1 |------` | -| `-------` | | `-------` | -`-------------------|-------------------` `-----------------|-----------------` - | | - ,-------, ,-------, - | C\_0 | | C\_1 | - `-------` `-------` +,-------------------------------------, ,---------------------------------, +| Per Instance of C\_0 | | Per Instance of C\_1 | +| | | | +| ,---------, ,---------, ,---------, | | ,---------, ,---------, | +| | V\_0\^0 | | V\_0\^1 | | V\_0\^2 | | | | V\_1\^0 | | V\_1\^1 | | +| `---------` `---------` `---------` | | `---------` `---------` | +| | | | | | | | | +| | ,-------, | | | | ,-------, | | +| `-------| P\_0 |-------` | | `-----| P\_1 |-----` | +| `-------` | | `-------` | +`------------------|------------------` `----------------|----------------` + | | + ,-------, ,-------, + | C\_0 | | C\_1 | + `-------` `-------` [\_ denotes subscript and \^ denotes superscript] diff --git a/doc/impl-details.texi b/doc/impl-details.texi index 619c197..e9a19de 100644 --- a/doc/impl-details.texi +++ b/doc/impl-details.texi @@ -1,28 +1,28 @@ @c This document is part of the GNU ease.js manual. -@c Copyright (C) 2011, 2012, 2013 Mike Gerwitz +@c Copyright (C) 2011, 2012, 2013, 2014 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''. +@c under the terms of the GNU Free Documentation License, Version 1.3 or +@c any later version published by the Free Software Foundation; with no +@c Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. +@c A copy of the license is included in the section entitled ``GNU Free +@c 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. +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. +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:: @@ -33,23 +33,24 @@ provide some details and rationale behind ease.js. @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 +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. +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. +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. +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:: @@ -61,27 +62,29 @@ inconsistent implementation. @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. +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 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. +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. +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 @@ -99,112 +102,120 @@ make it look like an opening block. 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. +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 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. +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. +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. +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 -(@pxref{Hacking Around the Issue of Encapsulation}). However, it quickly became -apparent that this method of storing data, although excellent for protecting it -from being manipulated, caused what appeared to be memory leaks in long-running -software. These were in fact not memory leaks, but ease.js keeping references to -class data with no idea when to free them. +(@pxref{Hacking Around the Issue of Encapsulation}). However, it quickly +became apparent that this method of storing data, although excellent for +protecting it from being manipulated, caused what appeared to be memory +leaks in long-running software. These were in fact not memory leaks, but +ease.js keeping references to class data with no idea when to free them. -To solve this issue, all class data is stored within the class itself (that is, -the constructor in JavaScript terms). They are stored in obscure variables that -are non-enumerable and subject to change in future releases. This ensures that -developers cannot rely on using them for reflection purposes or for manipulating -class data during runtime. This is important, since looking at such members can -give access to protected and private instance data. In the future, the names may -be randomly chosen at runtime to further mitigate exploits. Until that time, -developers should be aware of potential security issues. +To solve this issue, all class data is stored within the class itself (that +is, the constructor in JavaScript terms). They are stored in obscure +variables that are non-enumerable and subject to change in future releases. +This ensures that developers cannot rely on using them for reflection +purposes or for manipulating class data during runtime. This is important, +since looking at such members can give access to protected and private +instance data. In the future, the names may be randomly chosen at runtime to +further mitigate exploits. Until that time, developers should be aware of +potential security issues. 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. +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. +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. +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. +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. -One difference from PHP is the means of preventing instantiation. In PHP, if the -constructor is declared as non-public, then an error will be raised when the -developer attempts to instantiate the class. ease.js did not go this route, as -the method seems cryptic. Instead, an exception should be thrown in the -constructor if the developer doesn't wish the class to be instantiated. In the -future, a common method may be added for consistency/convenience. +One difference from PHP is the means of preventing instantiation. In PHP, if +the constructor is declared as non-public, then an error will be raised when +the developer attempts to instantiate the class. ease.js did not go this +route, as the method seems cryptic. Instead, an exception should be thrown +in the constructor if the developer doesn't wish the class to be +instantiated. In the future, a common method may be added for +consistency/convenience. -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 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. +The constructor is called after all initialization tasks have been +completed. @node Static Implementation @subsection Static Implementation -The decisions behind ease.js's static implementation were very difficult. More -thought and time was spent on paper designing how the static implementation -should be represented than most other features in the project. The reason for -this is not because the concept of static members is complicated. Rather, it is -due to limitations of pre-ECMAScript 5 engines. +The decisions behind ease.js's static implementation were very difficult. +More thought and time was spent on paper designing how the static +implementation should be represented than most other features in the +project. The reason for this is not because the concept of static members is +complicated. Rather, it is due to limitations of pre-ECMAScript 5 engines. @subsubsection How Static Members Are Supposed To Work -The first insight into the problems a static implementation would present was -the concept itself. Take any common Object-Oriented language such as C++, Java, -or even PHP. Static members are inherited by subtypes @emph{by reference}. What -does this mean? Consider two classes: @var{Foo} and @var{SubFoo}, the latter of -which inherits from the former. @var{Foo} defines a static property @var{count} -to be incremented each time the class is instantiated. The subtype @var{SubFoo}, -when instantiated (assuming the constructor is not overridden), would increment -that very same count. Therefore, we can represent this by stating that -@samp{Foo.count === SubFoo.count}. In the example below, we demonstrate this -concept in pseudocode: +The first insight into the problems a static implementation would present +was the concept itself. Take any common Object-Oriented language such as +C++, Java, or even PHP. Static members are inherited by subtypes @emph{by +reference}. What does this mean? Consider two classes: @var{Foo} and +@var{SubFoo}, the latter of which inherits from the former. @var{Foo} +defines a static property @var{count} to be incremented each time the class +is instantiated. The subtype @var{SubFoo}, when instantiated (assuming the +constructor is not overridden), would increment that very same count. +Therefore, we can represent this by stating that @samp{Foo.count === +SubFoo.count}. In the example below, we demonstrate this concept in +pseudocode: @float Figure, f:static-ref-pseudocode @verbatim @@ -221,11 +232,11 @@ concept in pseudocode: @end float As you may imagine, this is a problem. The above example does not look very -JS-like. That is because it isn't. JS does not provide a means for variables to -share references to the same primitive. In fact, even Objects are passed by -value in the sense that, if the variable is reassigned, the other variable -remains unaffected. The concept we are looking to support is similar to a -pointer in C/C++, or a reference in PHP. +JS-like. That is because it isn't. JS does not provide a means for variables +to share references to the same primitive. In fact, even Objects are passed +by value in the sense that, if the variable is reassigned, the other +variable remains unaffected. The concept we are looking to support is +similar to a pointer in C/C++, or a reference in PHP. We have no such luxury. @@ -264,10 +275,11 @@ supertype: @caption{Emulating references with getters/setters (proxy)} @end float -This comes with considerable overhead when compared to accessing the properties -directly (in fact, at the time of writing this, V8 doesn't even attempt to -optimize calls to getters/setters, so it is even slower than invoking accessor -methods). That point aside, it works well and accomplishes what we need it to. +This comes with considerable overhead when compared to accessing the +properties directly (in fact, at the time of writing this, V8 doesn't even +attempt to optimize calls to getters/setters, so it is even slower than +invoking accessor methods). That point aside, it works well and accomplishes +what we need it to. There's just one problem. @emph{This does not work in pre-ES5 environments!} ease.js needs to support older environments, falling back to ensure that @@ -275,18 +287,18 @@ everything operates the same (even though features such as visibility aren't present). This means that we cannot use this proxy implementation. It is used for -visibility in class instances, but that is because a fallback is possible. It is -not possible to provide a fallback that works with two separate objects. If -there were, we wouldn't have this problem in the first place. +visibility in class instances, but that is because a fallback is possible. +It is not possible to provide a fallback that works with two separate +objects. If there were, we wouldn't have this problem in the first place. @subsubsection Deciding On a Compromise A number of options were available regarding how static properties should be -implemented. Methods are not a problem -- they are only accessed by reference, -never written to. Therefore, they can keep their convenient @samp{Foo.method()} -syntax. Unfortunately, that cannot be the case for properties without the -ability to implement a proxy through the use of getters/setters (which, as -aforementioned, requires the services of ECMAScript 5, which is not available in -older environments). +implemented. Methods are not a problem -- they are only accessed by +reference, never written to. Therefore, they can keep their convenient +@samp{Foo.method()} syntax. Unfortunately, that cannot be the case for +properties without the ability to implement a proxy through the use of +getters/setters (which, as aforementioned, requires the services of +ECMAScript 5, which is not available in older environments). The choices were has follows: @@ -295,35 +307,38 @@ The choices were has follows: Add another object to be shared between classes (e.g. @samp{Foo.$}). @item -Do not inherit by reference. Each subtype would have their own distinct value. +Do not inherit by reference. Each subtype would have their own distinct +value. @item -Access properties via an accessor method (e.g. @samp{Foo.$('var')}), allowing us -to properly proxy much like a getter/setter. +Access properties via an accessor method (e.g. @samp{Foo.$('var')}), +allowing us to properly proxy much like a getter/setter. @end enumerate There are problems with all of the above options. The first option, which involves sharing an object, would cause awkward inheritance in the case of a -fallback. Subtypes would set their static properties on the object, which would -make that property available to the @emph{supertype}! That is tolerable in the -case of a fallback. However, the real problem lies in two other concepts: when a -class has two subtypes that attempt to define a property with the same name, or -when a subtype attempts to override a property. The former would cause both -subtypes (which are entirely separate from one-another, with the exception of -sharing the same parent) to share the same values, which is unacceptable. The -latter case can be circumvented by simply preventing overriding of static -properties, but the former just blows this idea out of the water entirely. +fallback. Subtypes would set their static properties on the object, which +would make that property available to the @emph{supertype}! That is +tolerable in the case of a fallback. However, the real problem lies in two +other concepts: when a class has two subtypes that attempt to define a +property with the same name, or when a subtype attempts to override a +property. The former would cause both subtypes (which are entirely separate +from one-another, with the exception of sharing the same parent) to share +the same values, which is unacceptable. The latter case can be circumvented +by simply preventing overriding of static properties, but the former just +blows this idea out of the water entirely. -The second option is to @emph{not} inherit by reference. This was the initial -implementation (due to JavaScript limitations) until it was realized that this -caused far too many inconsistencies between other Object-Oriented languages. -There is no use in introducing a different implementation when we are attempting -to mirror classic Object-Oriented principals to present a familiar paradigm to -developers. Given this inconsistency alone, this option simply will not work. +The second option is to @emph{not} inherit by reference. This was the +initial implementation (due to JavaScript limitations) until it was realized +that this caused far too many inconsistencies between other Object-Oriented +languages. There is no use in introducing a different implementation when +we are attempting to mirror classic Object-Oriented principals to present a +familiar paradigm to developers. Given this inconsistency alone, this option +simply will not work. The final option is to provide an accessor method, much like the style of -jQuery. This would serve as an ugly alternative for getters/setters. It would -operate as follows: +jQuery. This would serve as an ugly alternative for getters/setters. It +would operate as follows: @float Figure, f:static-accessor-impl @verbatim @@ -348,7 +363,8 @@ It provides an implementation that is @emph{consistent with other Object-Oriented languages}. This is the most important point. @item -The accessor method parameter style is common in other frameworks like jQuery. +The accessor method parameter style is common in other frameworks like +jQuery. @item The method name (@var{$}) is commonly used to denote a variable in scripting @@ -358,65 +374,66 @@ languages (such as PHP and shells, or to denote a scalar in Perl). It works consistently in ES5 and pre-ES5 environments alike. @end itemize -So, although the syntax is inconsistent with the rest of the framework, it does -address all of our key requirements. This makes it a viable option for our -implementation. +So, although the syntax is inconsistent with the rest of the framework, it +does address all of our key requirements. This makes it a viable option for +our implementation. @subsubsection Appeasing ES5-Only Developers -There is another argument to be had. ease.js is designed to operate across all -major browsers for all major versions, no matter how ridiculous (e.g. Internet -Explorer 5.5), so long as it does not require unreasonable development effort. -That is great and all, but what about those developers who are developing -@emph{only} for an ECMAScript 5 environment? This includes developers leveraging -modern HTML 5 features and those using Node.js who do not intend to share code -with pre-ES5 clients. Why should they suffer from an ugly, unnecessary syntax -when a beautiful, natural [and elegant] implementation is available using -proxies via getters/setters? +There is another argument to be had. ease.js is designed to operate across +all major browsers for all major versions, no matter how ridiculous (e.g. +Internet Explorer 5.5), so long as it does not require unreasonable +development effort. That is great and all, but what about those developers +who are developing @emph{only} for an ECMAScript 5 environment? This +includes developers leveraging modern HTML 5 features and those using +Node.js who do not intend to share code with pre-ES5 clients. Why should +they suffer from an ugly, unnecessary syntax when a beautiful, natural [and +elegant] implementation is available using proxies via getters/setters? There are certainly two sides to this argument. On one hand, it is perfectly -acceptable to request a natural syntax if it is supported. On the other hand, -this introduces a number of problems: +acceptable to request a natural syntax if it is supported. On the other +hand, this introduces a number of problems: @itemize @item This may make libraries written using ease.js unportable (to older -environments). If written using an ES5-only syntax, they would have no way to -fall back for static properties. +environments). If written using an ES5-only syntax, they would have no way +to fall back for static properties. @item -The syntax differences could be very confusing, especially to those beginning to -learn ease.js. They may not clearly understand the differences, or may go to use -a library in their own code, and find that things do not work as intended. -Code examples would also have to make clear note of what static syntax they -decided to use. It adds a layer of complexity. +The syntax differences could be very confusing, especially to those +beginning to learn ease.js. They may not clearly understand the differences, +or may go to use a library in their own code, and find that things do not +work as intended. Code examples would also have to make clear note of what +static syntax they decided to use. It adds a layer of complexity. @end itemize Now, those arguing for the cleaner syntax can also argue that all newer -environments moving forward will support the clean, ES5-only syntax, therefore -it would be beneficial to have. Especially when used for web applications that -can fall back to an entirely different implementation or refuse service entirely -to older browsers. Why hold ease.js back for those stragglers if there's no -intent on ever supporting them? +environments moving forward will support the clean, ES5-only syntax, +therefore it would be beneficial to have. Especially when used for web +applications that can fall back to an entirely different implementation or +refuse service entirely to older browsers. Why hold ease.js back for those +stragglers if there's no intent on ever supporting them? -Both arguments are solid. Ultimately, ease.js will likely favor the argument of -implementing the cleaner syntax by providing a runtime flag. If enabled, static -members will be set using proxies. If not, it will fall back to the uglier -implementation using the accessor method. If the environment doesn't support the -flag when set, ease.js will throw an error and refuse to run, or will invoke a -fallback specified by the developer to run an alternative code base that uses -the portable, pre-ES5 syntax. +Both arguments are solid. Ultimately, ease.js will likely favor the argument +of implementing the cleaner syntax by providing a runtime flag. If enabled, +static members will be set using proxies. If not, it will fall back to the +uglier implementation using the accessor method. If the environment doesn't +support the flag when set, ease.js will throw an error and refuse to run, or +will invoke a fallback specified by the developer to run an alternative code +base that uses the portable, pre-ES5 syntax. -This decision will ultimately be made in the future. For the time being, ease.js -will support and encourage use of the portable static property syntax. +This decision will ultimately be made in the future. For the time being, +ease.js will support and encourage use of the portable static property +syntax. @node Visibility Implementation @section Visibility Implementation One of the major distinguishing factors of ease.js is its full visibility -support (@pxref{Access Modifiers}). This feature was the main motivator behind -the project. Before we can understand the use of this feature, we have to -understand certain limitations of JavaScript and how we may be able to work -around them. +support (@pxref{Access Modifiers}). This feature was the main motivator +behind the project. Before we can understand the use of this feature, we +have to understand certain limitations of JavaScript and how we may be able +to work around them. @menu * Encapsulation In JavaScript:: @@ -430,7 +447,8 @@ around them. @subsection Encapsulation In JavaScript Encapsulation is a cornerstone of many strong software development paradigms (@pxref{Encapsulation}). This concept is relatively simply to achieve using -closures in JavaScript, as shown in the following example stack implementation: +closures in JavaScript, as shown in the following example stack +implementation: @float Figure, f:js-encapsulation-ex @verbatim @@ -457,21 +475,22 @@ stack.pop(); // foo @caption{Encapsulation example using closures in JavaScript} @end float -Because functions introduce scope in JavaScript, data can be hidden within them. -In @ref{f:js-encapsulation-ex} above, a self-executing function is used to -encapsulate the actual data in the stack (@var{data}). The function accepts a -single argument, which will hold the functions used to push and pop values -to/from the stack respectively. These functions are closures that have access to -the @var{data} variable, allowing them to alter its data. However, nothing -outside of the self-executing function has access to the data. Therefore, we -present the user with an API that allows them to push/pop from the stack, but -never allows them to see what data is actually @emph{in} the stack@footnote{The -pattern used in the stack implementation is commonly referred to as the -@dfn{module} pattern and is the same concept used by CommonJS. Another common -implementation is to return an object containing the functions from the -self-executing function, rather than accepting an object to store the values in. -We used the former implementation here for the sake of clarity and because it -more closely represents the syntax used by CommonJS.}. +Because functions introduce scope in JavaScript, data can be hidden within +them. In @ref{f:js-encapsulation-ex} above, a self-executing function is +used to encapsulate the actual data in the stack (@var{data}). The function +accepts a single argument, which will hold the functions used to push and +pop values to/from the stack respectively. These functions are closures that +have access to the @var{data} variable, allowing them to alter its data. +However, nothing outside of the self-executing function has access to the +data. Therefore, we present the user with an API that allows them to +push/pop from the stack, but never allows them to see what data is actually +@emph{in} the stack@footnote{The pattern used in the stack implementation is +commonly referred to as the @dfn{module} pattern and is the same concept +used by CommonJS. Another common implementation is to return an object +containing the functions from the self-executing function, rather than +accepting an object to store the values in. We used the former +implementation here for the sake of clarity and because it more closely +represents the syntax used by CommonJS.}. Let's translate some of the above into Object-Oriented terms: @@ -481,10 +500,10 @@ Let's translate some of the above into Object-Oriented terms: @item @var{stack} is a Singleton. @end itemize -We can take this a bit further by defining a @code{Stack} prototype so that we -can create multiple instances of our stack implementation. A single instance -hardly seems useful for reuse. However, in attempting to do so, we run into a -bit of a problem: +We can take this a bit further by defining a @code{Stack} prototype so that +we can create multiple instances of our stack implementation. A single +instance hardly seems useful for reuse. However, in attempting to do so, we +run into a bit of a problem: @float Figure, f:js-proto-inst-noencapsulate @verbatim @@ -523,14 +542,14 @@ encapsulation} By defining our methods on the prototype and our data in the constructor, we have created a bit of a problem. Although the data is easy to work with, @emph{it is no longer encapsulated}. The @var{_data} property is now public, -accessible for the entire work to inspect and modify. As such, a common practice -in JavaScript is to simply declare members that are "supposed to be" private -with an underscore prefix, as we have done above, and then trust that nobody -will make use of them. Not a great solution. +accessible for the entire work to inspect and modify. As such, a common +practice in JavaScript is to simply declare members that are "supposed to +be" private with an underscore prefix, as we have done above, and then trust +that nobody will make use of them. Not a great solution. -Another solution is to use a concept called @dfn{privileged members}, which uses -closures defined in the constructor rather than functions defined in the -prototype: +Another solution is to use a concept called @dfn{privileged members}, which +uses closures defined in the constructor rather than functions defined in +the prototype: @float Figure, f:js-privileged-members @verbatim @@ -560,39 +579,40 @@ console.log( inst._data ); // undefined @end float You may notice a strong similarity between @ref{f:js-encapsulation-ex} and -@ref{f:js-privileged-members}. They are doing essentially the same thing, the -only difference being that @ref{f:js-encapsulation-ex} is returning a single -object and @ref{f:js-privileged-members} represents a constructor that may be -instantiated. +@ref{f:js-privileged-members}. They are doing essentially the same thing, +the only difference being that @ref{f:js-encapsulation-ex} is returning a +single object and @ref{f:js-privileged-members} represents a constructor +that may be instantiated. -When using privileged members, one would define all members that need access to -such members in the constructor and define all remaining members in the +When using privileged members, one would define all members that need access +to such members in the constructor and define all remaining members in the prototype. However, this introduces a rather large problem that makes this design decision a poor one in practice: @emph{Each time @var{Stack} is instantiated, @var{push} and @var{pop} have to be redefined, taking up -additional memory and CPU cycles}. Those methods will be kept in memory until -the instance of @var{Stack} is garbage collected. +additional memory and CPU cycles}. Those methods will be kept in memory +until the instance of @var{Stack} is garbage collected. -In @ref{f:js-privileged-members}, these considerations may not seem like much of -an issue. However, consider a constructor that defines tens of methods and could -potentially have hundreds of instances. For this reason, you will often see the -concepts demonstrated in @ref{f:js-proto-inst-noencapsulate} used more -frequently in libraries that have even modest performance requirements. +In @ref{f:js-privileged-members}, these considerations may not seem like +much of an issue. However, consider a constructor that defines tens of +methods and could potentially have hundreds of instances. For this reason, +you will often see the concepts demonstrated in +@ref{f:js-proto-inst-noencapsulate} used more frequently in libraries that +have even modest performance requirements. @node Hacking Around the Issue of Encapsulation @subsection Hacking Around the Issue of Encapsulation -Since neither @ref{f:js-encapsulation-ex} nor @ref{f:js-privileged-members} are -acceptable implementations for strong Classical Object-Oriented code, another -solution is needed. Based on what we have seen thus far, let's consider our -requirements: +Since neither @ref{f:js-encapsulation-ex} nor @ref{f:js-privileged-members} +are acceptable implementations for strong Classical Object-Oriented code, +another solution is needed. Based on what we have seen thus far, let's +consider our requirements: @itemize @item -Our implementation must not break encapsulation. That is - we should be +Our implementation must not break encapsulation. That is---we should be enforcing encapsulation, not simply trusting our users not to touch. @item -We must be gentle with our memory allocations and processing. This means placing -@emph{all} methods within the prototype. +We must be gentle with our memory allocations and processing. This means +placing @emph{all} methods within the prototype. @item We should not require any changes to how the developer uses the constructor/object. It should operate just like any other construct in @@ -602,11 +622,11 @@ JavaScript. We can accomplish the above by using the encapsulation concepts from @ref{f:js-encapsulation-ex} and the same prototype model demonstrated in @ref{f:js-proto-inst-noencapsulate}. The problem with -@ref{f:js-encapsulation-ex}, which provided proper encapsulation, was that it -acted as a Singleton. We could not create multiple instances of it and, even if -we could, they would end up sharing the same data. To solve this problem, we -need a means of distinguishing between each of the instances so that we can -access the data of each separately: +@ref{f:js-encapsulation-ex}, which provided proper encapsulation, was that +it acted as a Singleton. We could not create multiple instances of it and, +even if we could, they would end up sharing the same data. To solve this +problem, we need a means of distinguishing between each of the instances so +that we can access the data of each separately: @float Figure, f:js-encapsulate-instance @verbatim @@ -617,8 +637,8 @@ var Stack = ( function() var S = function() { - // set the instance id of this instance, then increment it to ensure the - // value is unique for the next instance + // set the instance id of this instance, then increment it to ensure + // the value is unique for the next instance this.__iid = iid++; // initialize our data for this instance @@ -654,23 +674,24 @@ stack2.pop(); // bar @caption{Encapsulating data per instance} @end float -This would seem to accomplish each of our above goals. Our implementation does -not break encapsulation, as nobody can get at the data. Our methods are part of -the @var{Stack} prototype, so we are not redefining it with each instance, -eliminating our memory and processing issues. Finally, @var{Stack} instances can -be instantiated and used just like any other object in JavaScript; the developer -needn't adhere to any obscure standards in order to emulate encapsulation. +This would seem to accomplish each of our above goals. Our implementation +does not break encapsulation, as nobody can get at the data. Our methods are +part of the @var{Stack} prototype, so we are not redefining it with each +instance, eliminating our memory and processing issues. Finally, @var{Stack} +instances can be instantiated and used just like any other object in +JavaScript; the developer needn't adhere to any obscure standards in order +to emulate encapsulation. -Excellent! However, our implementation does introduce a number of issues that we -hadn't previously considered: +Excellent! However, our implementation does introduce a number of issues +that we hadn't previously considered: @itemize @item -Our implementation is hardly concise. Working with our ``private'' properties -requires that we add ugly instance lookup code@footnote{We could encapsulate -this lookup code, but we would then have the overhead of an additional method -call with very little benefit; we cannot do something like: @samp{this.stack}.}, -obscuring the actual domain logic. +Our implementation is hardly concise. Working with our ``private'' +properties requires that we add ugly instance lookup code@footnote{We could +encapsulate this lookup code, but we would then have the overhead of an +additional method call with very little benefit; we cannot do something +like: @samp{this.stack}.}, obscuring the actual domain logic. @item Most importantly: @emph{this implementation introduces memory leaks}. @end itemize @@ -678,68 +699,71 @@ Most importantly: @emph{this implementation introduces memory leaks}. What do we mean by ``memory leaks''? Consider the usage example in @ref{f:js-encapsulate-instance}. What happens when were are done using @var{stack1} and @var{stack2} and they fall out of scope? They will be GC'd. -However, take a look at our @var{idata} variable. The garbage collector will not -know to free up the data for our particular instance. Indeed, it cannot, because -we are still holding a reference to that data as a member of the @var{idata} -array. +However, take a look at our @var{idata} variable. The garbage collector will +not know to free up the data for our particular instance. Indeed, it cannot, +because we are still holding a reference to that data as a member of the +@var{idata} array. -Now imagine that we have a long-running piece of software that makes heavy use -of @var{Stack}. This software will use thousands of instances throughout its -life, but they are used only briefly and then discarded. Let us also imagine -that the stacks are very large, perhaps holding hundreds of elements, and that -we do not necessarily @code{pop()} every element off of the stack before we -discard it. +Now imagine that we have a long-running piece of software that makes heavy +use of @var{Stack}. This software will use thousands of instances throughout +its life, but they are used only briefly and then discarded. Let us also +imagine that the stacks are very large, perhaps holding hundreds of +elements, and that we do not necessarily @code{pop()} every element off of +the stack before we discard it. -Imagine that we examine the memory usage throughout the life of this software. -Each time a stack is used, additional memory will be allocated. Each time we -@code{push()} an element onto the stack, additional memory is allocated for that -element. Because our @var{idata} structure is not freed when the @var{Stack} -instance goes out of scope, we will see the memory continue to rise. The memory -would not drop until @var{Stack} itself falls out of scope, which may not be -until the user navigates away from the page. +Imagine that we examine the memory usage throughout the life of this +software. Each time a stack is used, additional memory will be allocated. +Each time we @code{push()} an element onto the stack, additional memory is +allocated for that element. Because our @var{idata} structure is not freed +when the @var{Stack} instance goes out of scope, we will see the memory +continue to rise. The memory would not drop until @var{Stack} itself falls +out of scope, which may not be until the user navigates away from the page. -From our perspective, this is not a memory leak. Our implementation is working -exactly as it was developer. However, to the user of our stack implementation, -this memory management is out of their control. From their perspective, this is -indeed a memory leak that could have terrible consequences on their software. +From our perspective, this is not a memory leak. Our implementation is +working exactly as it was developer. However, to the user of our stack +implementation, this memory management is out of their control. From their +perspective, this is indeed a memory leak that could have terrible +consequences on their software. -This method of storing instance data was ease.js's initial ``proof-of-concept'' -implementation (@pxref{Class Storage}). Clearly, this was not going to work; -some changes to this implementation were needed. +This method of storing instance data was ease.js's initial +``proof-of-concept'' implementation (@pxref{Class Storage}). Clearly, this +was not going to work; some changes to this implementation were needed. @anchor{Instance Memory Considerations} @subsubsection Instance Memory Considerations -JavaScript does not provide destructors to let us know when an instance is about -to be GC'd, so we unfortunately cannot know when to free instance data from -memory in @ref{f:js-encapsulate-instance}. We are also not provided with an API -that can return the reference count for a given object. We could provide a -method that the user could call when they were done with the object, but that is -not natural to a JavaScript developer and they could easily forget to call the -method. +JavaScript does not provide destructors to let us know when an instance is +about to be GC'd, so we unfortunately cannot know when to free instance data +from memory in @ref{f:js-encapsulate-instance}. We are also not provided +with an API that can return the reference count for a given object. We could +provide a method that the user could call when they were done with the +object, but that is not natural to a JavaScript developer and they could +easily forget to call the method. -As such, it seems that the only solution for this rather large issue is to store -instance data on the instance itself so that it will be freed with the instance -when it is garbage collected (remember, we decided that privileged members were -not an option in the discussion of @ref{f:js-privileged-members}). Hold on - we -already did that in @ref{f:js-proto-inst-noencapsulate}; that caused our data to -be available publicly. How do we approach this situation? +As such, it seems that the only solution for this rather large issue is to +store instance data on the instance itself so that it will be freed with the +instance when it is garbage collected (remember, we decided that privileged +members were not an option in the discussion of +@ref{f:js-privileged-members}). Hold on - we already did that in +@ref{f:js-proto-inst-noencapsulate}; that caused our data to be available +publicly. How do we approach this situation? -If we are adding data to an instance itself, there is no way to prevent it from -being accessed in some manner, making true encapsulation impossible. The only -options are to obscure it as best as possible, to make it too difficult to -access in any sane implementation. For example: +If we are adding data to an instance itself, there is no way to prevent it +from being accessed in some manner, making true encapsulation impossible. +The only options are to obscure it as best as possible, to make it too +difficult to access in any sane implementation. For example: @itemize @item -The property storing the private data could be made non-enumerable, requiring -the use of a debugger or looking at the source code to determine the object -name. +The property storing the private data could be made non-enumerable, +requiring the use of a debugger or looking at the source code to determine +the object name. @itemize @item This would work only with ECMAScript 5 and later environments. @end itemize @item We could store all private data in an obscure property name, such as -@var{___$$priv$$___}, which would make it clear that it should not be accessed. +@var{___$$priv$$___}, which would make it clear that it should not be +accessed. @itemize @item We could take that a step further and randomize the name, making it very @@ -749,9 +773,9 @@ We could store all private data in an obscure property name, such as @end itemize @end itemize -Regardless, it is clear that our data will only be ``encapsulated'' in the sense -that it will not be available conveniently via a public API. Let's take a look -at how something like that may work: +Regardless, it is clear that our data will only be ``encapsulated'' in the +sense that it will not be available conveniently via a public API. Let's +take a look at how something like that may work: @float Figure, f:js-obscure-private @verbatim @@ -762,8 +786,8 @@ var Stack = ( function() var S = function() { - // define a non-enumerable property to store our private data (will only - // work in ES5+ environments) + // define a non-enumerable property to store our private data (will + // only work in ES5+ environments) Object.defineProperty( this, _privname, { enumerable: false, writable: false, @@ -794,31 +818,33 @@ var inst = new Stack(); inst.push( 'foo' ); inst.pop(); // foo @end verbatim -@caption{Using a random, non-enumerable property name to store private members} +@caption{Using a random, non-enumerable property name to store private +members} @end float -Now we are really starting to hack around what JavaScript provides for us. We -seem to be combining the encapsulation issues presented in +Now we are really starting to hack around what JavaScript provides for us. +We seem to be combining the encapsulation issues presented in @ref{f:js-proto-inst-noencapsulate} and the obscurity demonstrated in -@ref{f:js-encapsulate-instance}. In addition, we our implementation depends on -ECMAScript 5 (ideally, we would detect that and fall back to normal, enumerable -properties in pre-ES5 environments, which ease.js does indeed do). This seems to -be a case of encapsulation through obscurity@footnote{A play on ``security -through obscurity''.}. While our implementation certainly makes it difficult to -get at the private member data, it is also very obscure and inconvenient to work -with. Who wants to write Object-Oriented code like that? +@ref{f:js-encapsulate-instance}. In addition, we our implementation depends +on ECMAScript 5 (ideally, we would detect that and fall back to normal, +enumerable properties in pre-ES5 environments, which ease.js does indeed +do). This seems to be a case of encapsulation through obscurity@footnote{A +play on ``security through obscurity''.}. While our implementation certainly +makes it difficult to get at the private member data, it is also very +obscure and inconvenient to work with. Who wants to write Object-Oriented +code like that? @subsubsection Other Considerations We have conveniently omitted a number of other important factors in our -discussion thus far. Before continuing, they deserve some mention and careful -consideration. +discussion thus far. Before continuing, they deserve some mention and +careful consideration. -How would we implement private methods? We could add them to our private member -object, just as we defined @var{stack} in @ref{f:js-obscure-private}, but that -would cause it to be redefined with each instance, raising the same issues that -were discussed with @ref{f:js-privileged-members}. Therefore, we would have to -define them in a separate ``prototype'', if you will, that only we have access -to: +How would we implement private methods? We could add them to our private +member object, just as we defined @var{stack} in @ref{f:js-obscure-private}, +but that would cause it to be redefined with each instance, raising the same +issues that were discussed with @ref{f:js-privileged-members}. Therefore, we +would have to define them in a separate ``prototype'', if you will, that +only we have access to: @float Figure, f:js-obscure-private-methods @verbatim @@ -829,8 +855,8 @@ var Stack = ( function() var S = function() { - // define a non-enumerable property to store our private data (will only - // work in ES5+ environments) + // define a non-enumerable property to store our private data (will + // only work in ES5+ environments) Object.defineProperty( this, _privname, { // ... (see previous example) } ); @@ -840,8 +866,8 @@ var Stack = ( function() var priv_methods = { getStack: function() { - // note that, in order for 'this' to be bound to our instance, it - // must be passed as first argument to call() or apply() + // note that, in order for 'this' to be bound to our instance, + // it must be passed as first argument to call() or apply() return this[ _privname ].stack; }, }; @@ -872,31 +898,33 @@ inst.pop(); // foo @end float While this does solve our problem, it further reduces code clarity. The -implementation in @ref{f:js-obscure-private-methods} is certainly a far cry from -something like @samp{this._getStack()}, which is all you would need to do in -ease.js. +implementation in @ref{f:js-obscure-private-methods} is certainly a far cry +from something like @samp{this._getStack()}, which is all you would need to +do in ease.js. Another consideration is a protected (@pxref{Access Modifiers}) member implementation, the idea being that subtypes should inherit both public and -protected members. Inheritance is not something that we had to worry about with -private members, so this adds an entirely new layer of complexity to the -implementation. This would mean somehow making a protected prototype available -to subtypes through the public prototype. Given our implementation in the -previous figures, this would likely mean an awkward call that somewhat -resembles: @samp{this[ _protname ].name}. +protected members. Inheritance is not something that we had to worry about +with private members, so this adds an entirely new layer of complexity to +the implementation. This would mean somehow making a protected prototype +available to subtypes through the public prototype. Given our implementation +in the previous figures, this would likely mean an awkward call that +somewhat resembles: @samp{this[ _protname ].name}. Although the implementations show in @ref{f:js-obscure-private} and -@ref{f:js-obscure-private-methods} represent a creative hack, this is precisely -one of the reasons ease.js was created - to encapsulate such atrocities that -would make code that is difficult to use, hard to maintain and easy to introduce -bugs. One shouldn't have to have a deep understanding of JavaScript's prototype -model in order to write the most elementary of Classical Object-Oriented code. -For example, the constructors in the aforementioned figures directly set up an -object in which to store private members. ease.js will do this for you before -calling the @code{__construct()} method. Furthermore, ease.js does not require +@ref{f:js-obscure-private-methods} represent a creative hack, this is +precisely one of the reasons ease.js was created - to encapsulate such +atrocities that would make code that is difficult to use, hard to maintain +and easy to introduce bugs. One shouldn't have to have a deep understanding +of JavaScript's prototype model in order to write the most elementary of +Classical Object-Oriented code. For example, the constructors in the +aforementioned figures directly set up an object in which to store private +members. ease.js will do this for you before calling the +@code{__construct()} method. Furthermore, ease.js does not require referencing that object directly, like we must do in our methods in -@ref{f:js-obscure-private}. Nor does ease.js have an awkward syntax for invoking -private methods. We will explore how this is handled in the following section. +@ref{f:js-obscure-private}. Nor does ease.js have an awkward syntax for +invoking private methods. We will explore how this is handled in the +following section. @node The Visibility Object @@ -929,24 +957,25 @@ inst.pop(); // foo @end float The above implementation is much less impressive looking than our prior -examples. What we have done is encapsulate the excess logic needed to emulate a -class and got right down to business. ease.js will take the class definition -above and generate an object much like we had done in the prior examples, with a -few improvements. +examples. What we have done is encapsulate the excess logic needed to +emulate a class and got right down to business. ease.js will take the class +definition above and generate an object much like we had done in the prior +examples, with a few improvements. -If you have not read over the previous sections, you are recommended to do so -before continuing in order to better understand the rationale and finer +If you have not read over the previous sections, you are recommended to do +so before continuing in order to better understand the rationale and finer implementation details. The secret behind ease.js's visibility implementation (@pxref{Access Modifiers}) is referred to internally as the @dfn{visibility object} (or, in -older commits and some notes, the @dfn{property object}). Consider the problem -regarding the verbosity of our private property accessors and method calls in -@ref{f:js-obscure-private-methods}. It would be much more convenient if the -properties and methods were bound to @var{this} so that they can be accessed -more naturally, as would be expected by a programmer familiar with classes in -other Classical Object-Oriented languages (@pxref{f:stack-using-easejs}). This -can be done using @code{call()} or @code{apply()}: +older commits and some notes, the @dfn{property object}). Consider the +problem regarding the verbosity of our private property accessors and method +calls in @ref{f:js-obscure-private-methods}. It would be much more +convenient if the properties and methods were bound to @var{this} so that +they can be accessed more naturally, as would be expected by a programmer +familiar with classes in other Classical Object-Oriented languages +(@pxref{f:stack-using-easejs}). This can be done using @code{call()} or +@code{apply()}: @float Figure, f:function-context @verbatim @@ -961,13 +990,13 @@ getName.call( obj ); // "foo" @caption{Calling a function within the context of a given object} @end float -@ref{f:function-context} demonstrates the concept we are referring to. Given an -arbitrary object @var{obj}, we can call any given method (in this case, +@ref{f:function-context} demonstrates the concept we are referring to. Given +an arbitrary object @var{obj}, we can call any given method (in this case, @code{getName()}, binding @var{this} to that object. This is precisely what ease.js does with each method call. To understand this process, we have to -explore two concepts: the visibility object itself and method wrapping. We will -start by discussing the visibility object in more detail and cover method -wrapping later on (@pxref{Method Wrapping}). +explore two concepts: the visibility object itself and method wrapping. We +will start by discussing the visibility object in more detail and cover +method wrapping later on (@pxref{Method Wrapping}). @menu * Visibility Object Implementation:: Design of the visibility object @@ -983,35 +1012,37 @@ The visibility object is mostly simply represented in the following diagram: @caption{Structure of the visibility object} @end float -Specifically, the visibility object is a prototype chain containing the private -members of the class associated with the method currently being invoked on the -current instance, its protected members (including those inherited from its -supertype) and the public members (also including those inherited from the -supertype). To accomplish this, the visibility object has the following -properties: +Specifically, the visibility object is a prototype chain containing the +private members of the class associated with the method currently being +invoked on the current instance, its protected members (including those +inherited from its supertype) and the public members (also including those +inherited from the supertype). To accomplish this, the visibility object has +the following properties: @itemize @item -The private object is @dfn{swappable} - that is, it is the only portion of the -prototype chain that is replaced between calls to various methods. +The private object is @dfn{swappable} - that is, it is the only portion of +the prototype chain that is replaced between calls to various methods. @itemize @item It is for this reason that the private object is placed atop the prototype - chain. This allows it to be swapped very cheaply by simply passing different - objects to be bound to @code{this}. + chain. This allows it to be swapped very cheaply by simply passing + different objects to be bound to @code{this}. @end itemize @item -Both the private and protected objects are initialized during instantiation by -the @code{__initProps()} method attached by @code{ClassBuilder} to each class -during definition. +Both the private and protected objects are initialized during instantiation +by the @code{__initProps()} method attached by @code{ClassBuilder} to each +class during definition. @itemize @item - Properties are cloned to ensure references are not shared between instances. + Properties are cloned to ensure references are not shared between + instances. @item - Methods are copied by reference, since their implementations are immutable. + Methods are copied by reference, since their implementations are + immutable. @item - This must be done because neither protected nor private members may be added - to the prototype chain of a class. + This must be done because neither protected nor private members may be + added to the prototype chain of a class. @itemize @item Doing so would effectively make them public. @@ -1020,21 +1051,21 @@ during definition. @end itemize @end itemize @item -Public members are a part of the class prototype chain as you would expect in -any conventional prototype. +Public members are a part of the class prototype chain as you would expect +in any conventional prototype. @itemize @item - Public @emph{properties} only are initialized by @code{__initProps()}, just as - private and protected properties, to ensure that no references are shared - between instances. + Public @emph{properties} only are initialized by @code{__initProps()}, + just as private and protected properties, to ensure that no references are + shared between instances. @end itemize @end itemize -As a consequence of the above, it is then clear that there must be a separate -visibility object (prototype chain) @emph{for each supertype of each instance}, -because there must be a separate private object for each subtype of each -instance. Let us consider for a moment why this is necessary with the following -sample of code: +As a consequence of the above, it is then clear that there must be a +separate visibility object (prototype chain) @emph{for each supertype of +each instance}, because there must be a separate private object for each +subtype of each instance. Let us consider for a moment why this is necessary +with the following sample of code: @float Figure, f:priv-swap-rationale @verbatim @@ -1065,23 +1096,23 @@ C0().getName(); // "Foo" @ref{f:priv-swap-rationale} demonstrates why the private object swapping@footnote{The term ``swapping'' can be a bit deceptive. While we are -swapping in the sense that we are passing an entirely new private object as the -context to a method, we are @emph{not} removing an object from the prototype chain and -adding another in place of it. They @emph{do}, however, share the same prototype -chain.} is indeed necessary. If a subtype does @emph{not} override a super -method that uses a private member, it is important that the private member be -accessible to the method when it is called. In @ref{f:priv-swap-rationale}, if -we did not swap out the object, @var{_name} would be undefined when invoked on -@var{C2}. +swapping in the sense that we are passing an entirely new private object as +the context to a method, we are @emph{not} removing an object from the +prototype chain and adding another in place of it. They @emph{do}, however, +share the same prototype chain.} is indeed necessary. If a subtype does +@emph{not} override a super method that uses a private member, it is +important that the private member be accessible to the method when it is +called. In @ref{f:priv-swap-rationale}, if we did not swap out the object, +@var{_name} would be undefined when invoked on @var{C2}. Given this new information, the implementation would more formally be -represented as a collection of objects @var{V} for class @var{C} and each of its -supertypes as denoted by @var{C\_n}, with @var{C\_0} representing the class -having been instantiated and any integer @var{n} > 0 representing the closest -supertype, such that each @var{V\_n} is associated with @var{C\_n}, -@var{V\_n\^x} is the visibility object bound to any method associated with class -@var{C\_x} and each @var{V} shares the same prototype chain @var{P\_n} for any -given instance of @var{C\_n}: +represented as a collection of objects @var{V} for class @var{C} and each of +its supertypes as denoted by @var{C\_n}, with @var{C\_0} representing the +class having been instantiated and any integer @var{n} > 0 representing the +closest supertype, such that each @var{V\_n} is associated with @var{C\_n}, +@var{V\_n\^x} is the visibility object bound to any method associated with +class @var{C\_x} and each @var{V} shares the same prototype chain @var{P\_n} +for any given instance of @var{C\_n}: @float Figure, f:visobj-collection @image{img/visobj-collection-wide} @@ -1099,30 +1130,30 @@ prototype of every @var{V\_n}. @var{C\_n} is re-used as the prototype for each @var{P\_n}. @end itemize -Consequently, on instantiation of class @var{C\_n}, we incur a performance hit -from @code{__initProps()} for the initialization of each member of @var{V\_x} -and @var{P\_x}, as well as each property of @var{C\_x}, recursively for each -value of @var{m} >= @var{x} >= @var{n} (that is, supertypes are initialized -first), where @var{m} is equal to the number of supertypes of class @var{C\_n} + -1.@footnote{There is room for optimization in this implementation, which -will be left for future versions of ease.js.} +Consequently, on instantiation of class @var{C\_n}, we incur a performance +hit from @code{__initProps()} for the initialization of each member of +@var{V\_x} and @var{P\_x}, as well as each property of @var{C\_x}, +recursively for each value of @var{m} >= @var{x} >= @var{n} (that is, +supertypes are initialized first), where @var{m} is equal to the number of +supertypes of class @var{C\_n} + 1.@footnote{There is room for optimization +in this implementation, which will be left for future versions of ease.js.} The instance stores a reference to each of the visibility objects @var{V}, -indexed by an internal class identifier (which is simply incremented for each -new class definition, much like we did with the instance id in -@ref{f:js-encapsulate-instance}). When a method is called, the visibility object -that matches the class identifier associated with the invoked method is then -passed as the context (bound to @var{this}) for that method (@pxref{Method -Wrapping}). +indexed by an internal class identifier (which is simply incremented for +each new class definition, much like we did with the instance id in +@ref{f:js-encapsulate-instance}). When a method is called, the visibility +object that matches the class identifier associated with the invoked method +is then passed as the context (bound to @var{this}) for that method +(@pxref{Method Wrapping}). @node Property Proxies @subsubsection Property Proxies -Astute readers may notice that the visibility implementation described in the -previous section (@pxref{Visibility Object Implementation}) has one critical -flaw stemming from how prototypes in JavaScript are implemented: setting a -property on the visibility object bound to the method will set the property on -that object, but @emph{not necessarily on its correct object}. The following -example will demonstrate this issue: +Astute readers may notice that the visibility implementation described in +the previous section (@pxref{Visibility Object Implementation}) has one +critical flaw stemming from how prototypes in JavaScript are implemented: +setting a property on the visibility object bound to the method will set the +property on that object, but @emph{not necessarily on its correct object}. +The following example will demonstrate this issue: @float Figure, f:proto-set-issue @verbatim @@ -1138,8 +1169,8 @@ var pub = { priv = function() {} ; -// set up our visibility object's prototype chain (we're leaving the protected -// layer out of the equation) +// set up our visibility object's prototype chain (we're leaving the +// protected layer out of the equation) priv.prototype = pub; // create our visibility object @@ -1170,17 +1201,19 @@ vis.foo; // "moo" Retrieving property values and invoking methods are not a problem. This is because values further down the prototype chain peek through ``holes'' in -objects further up the chain. Since @var{vis} in @ref{f:proto-set-issue} has no -value for property @var{foo} (note that a value of @code{undefined} is still a -value), it looks at its prototype, @var{pub}, and finds the value there. +objects further up the chain. Since @var{vis} in @ref{f:proto-set-issue} has +no value for property @var{foo} (note that a value of @code{undefined} is +still a value), it looks at its prototype, @var{pub}, and finds the value +there. -However, the story changes a bit when we try to set a value. When we assign a -value to member @var{foo} of @var{vis}, we are in fact setting the property on -@var{vis} itself, @emph{not} @var{pub}. This fills that aforementioned ``hole'', -masking the value further down the prototype chain (our value in @var{pub}). -This has the terrible consequence that if we were to set a public/protected -property value from within a method, it would only be accessible from within -that instance, for @emph{only that visibility object}. +However, the story changes a bit when we try to set a value. When we assign +a value to member @var{foo} of @var{vis}, we are in fact setting the +property on @var{vis} itself, @emph{not} @var{pub}. This fills that +aforementioned ``hole'', masking the value further down the prototype chain +(our value in @var{pub}). This has the terrible consequence that if we were +to set a public/protected property value from within a method, it would only +be accessible from within that instance, for @emph{only that visibility +object}. To summarize: @@ -1188,28 +1221,30 @@ To summarize: @item Methods are never an issue, as they are immutable (in the sense of a class). @item -Reading properties are never an issue; they properly ``peek'' through holes in -the prototype chain. +Reading properties are never an issue; they properly ``peek'' through holes +in the prototype chain. @item -Writing private values are never an issue, as they will be properly set on that -visibility object. The value needn't be set on any other visibility objects, -since private values are to remain exclusive to that instance within the context -of that class only (it should not be available to methods of supertypes). +Writing private values are never an issue, as they will be properly set on +that visibility object. The value needn't be set on any other visibility +objects, since private values are to remain exclusive to that instance +within the context of that class only (it should not be available to methods +of supertypes). @item -We run into issues when @emph{setting} public or protected values, as they are -not set on their appropriate object. +We run into issues when @emph{setting} public or protected values, as they +are not set on their appropriate object. @end itemize This issue is huge. Before ECMAScript 5, it may have been a show-stopper, -preventing us from using a familiar @code{this.prop} syntax within classes and -making the framework more of a mess than an elegant implementation. It is also -likely that this is the reason that frameworks like ease.js did not yet exist; -ECMAScript 5 and browsers that actually implement it are still relatively new. +preventing us from using a familiar @code{this.prop} syntax within classes +and making the framework more of a mess than an elegant implementation. It +is also likely that this is the reason that frameworks like ease.js did not +yet exist; ECMAScript 5 and browsers that actually implement it are still +relatively new. -Fortunately, ECMAScript 5 provides support for getters and setters. Using these, -we can create a proxy from our visibility object to the appropriate members of -the other layers (protected, public). Let us demonstrate this by building off of -@ref{f:proto-set-issue}: +Fortunately, ECMAScript 5 provides support for getters and setters. Using +these, we can create a proxy from our visibility object to the appropriate +members of the other layers (protected, public). Let us demonstrate this by +building off of @ref{f:proto-set-issue}: @float Figure, f:proto-getset @verbatim @@ -1239,16 +1274,17 @@ vis.foo; // "changed" @caption{Using getters/setters to proxy values to the appropriate object} @end float -The implementation in @ref{f:proto-getset} is precisely how ease.js implements -and @emph{enforces} the various levels of visibility.@footnote{One may wonder -why we implemented a getter in @ref{f:proto-getset} when we had no trouble -retrieving the value to begin with. In defining a @emph{setter} for @var{foo} on -object @var{vis}, we filled that ``hole'', preventing us from ``seeing through'' -into the prototype (@var{pub}). Unfortunately, that means that we must use a -getter in order to provide the illusion of the ``hole''.} This is both fortunate +The implementation in @ref{f:proto-getset} is precisely how ease.js +implements and @emph{enforces} the various levels of +visibility.@footnote{One may wonder why we implemented a getter in +@ref{f:proto-getset} when we had no trouble retrieving the value to begin +with. In defining a @emph{setter} for @var{foo} on object @var{vis}, we +filled that ``hole'', preventing us from ``seeing through'' into the +prototype (@var{pub}). Unfortunately, that means that we must use a getter +in order to provide the illusion of the ``hole''.} This is both fortunate and unfortunate; the project had been saved by getters/setters, but with a -slight performance penalty. In order to implement this proxy, the following must -be done: +slight performance penalty. In order to implement this proxy, the following +must be done: @itemize @item @@ -1256,52 +1292,54 @@ For each public property, proxy from the protected object to the public. @item For each protected property, proxy from the private object to the protected.@footnote{One may also notice that we are not proxying public -properties from the private member object to the public object. The reason for -this is that getters/setters, being functions, @emph{are} properly invoked when -nestled within the prototype chain. The reader may then question why ease.js did -not simply convert each property to a getter/setter, which would prevent the -need for proxying. The reason for this was performance - with the current -implementation, there is only a penalty for accessing public members from within -an instance, for example. However, accessing public members outside of the class -is as fast as normal property access. By converting all properties to -getters/setters, we would cause a performance hit across the board, which is -unnecessary.} +properties from the private member object to the public object. The reason +for this is that getters/setters, being functions, @emph{are} properly +invoked when nestled within the prototype chain. The reader may then +question why ease.js did not simply convert each property to a +getter/setter, which would prevent the need for proxying. The reason for +this was performance - with the current implementation, there is only a +penalty for accessing public members from within an instance, for example. +However, accessing public members outside of the class is as fast as normal +property access. By converting all properties to getters/setters, we would +cause a performance hit across the board, which is unnecessary.} @end itemize -Consequently, this means that accessing public properties from within the class -will be slower than accessing the property outside of the class. Furthermore, -accessing a protected property will @emph{always} incur a performance -hit@footnote{How much of a performance hit are we talking? This will depend on -environment. In the case of v8 (Node.js is used to run the performance tests -currently), getters/setters are not yet optimized (converted to machine code), -so they are considerably more slow than direct property access. +Consequently, this means that accessing public properties from within the +class will be slower than accessing the property outside of the class. +Furthermore, accessing a protected property will @emph{always} incur a +performance hit@footnote{How much of a performance hit are we talking? This +will depend on environment. In the case of v8 (Node.js is used to run the +performance tests currently), getters/setters are not yet optimized +(converted to machine code), so they are considerably more slow than direct +property access. -For example: on one system using v8, reading public properties externally took -only 0.0000000060s (direct access), whereas accessing the same property +For example: on one system using v8, reading public properties externally +took only 0.0000000060s (direct access), whereas accessing the same property internally took 0.0000001120s (through the proxy), which is a significant -(18.6x) slow-down. Run that test 500,000 times, as the performance test does, -and we're looking at 0.005s for direct access vs 0.056s for proxy access.}, -because it is always hidden behind the provide object and it cannot be accessed -from outside of the class. On the upside, accessing private members is fast (as -in - ``normal'' speed). This has the benefit of encouraging proper OO practices -by discouraging the use of public and protected properties. Note that methods, -as they are not proxied, do not incur the same performance hit. +(18.6x) slow-down. Run that test 500,000 times, as the performance test +does, and we're looking at 0.005s for direct access vs 0.056s for proxy +access.}, because it is always hidden behind the provide object and it +cannot be accessed from outside of the class. On the upside, accessing +private members is fast (as in - ``normal'' speed). This has the benefit of +encouraging proper OO practices by discouraging the use of public and +protected properties. Note that methods, as they are not proxied, do not +incur the same performance hit. Given the above implementation details, it is clear that ease.js has been -optimized for the most common use case, indicative of proper OO development - -the access of private properties from within classes, for which there will be no -performance penalty. +optimized for the most common use case, indicative of proper OO development +- the access of private properties from within classes, for which there will +be no performance penalty. @node Method Wrapping @subsection Method Wrapping The visibility object (@pxref{The Visibility Object}) is a useful tool for -organizing the various members, but we still need some means of binding it to a -method call. This is accomplished by wrapping each method in a closure that, -among other things@footnote{The closure also sets the @code{__super()} method -reference, if a super method exists, and returns the instance if @var{this} is -returned from the method.}, uses @code{apply()} to forward the arguments to the -method, binding @var{this} to the appropriate visibility object. This is very -similar to the ES5 @code{Function.bind()} call. +organizing the various members, but we still need some means of binding it +to a method call. This is accomplished by wrapping each method in a closure +that, among other things@footnote{The closure also sets the @code{__super()} +method reference, if a super method exists, and returns the instance if +@var{this} is returned from the method.}, uses @code{apply()} to forward the +arguments to the method, binding @var{this} to the appropriate visibility +object. This is very similar to the ES5 @code{Function.bind()} call. The following example demonstrates in an overly-simplistic way how ease.js handles class definitions and method wrapping.@footnote{ease.js, of course, @@ -1311,8 +1349,8 @@ brevity, we simply provide one in our example.} @float Figure, f:method-wrapping @verbatim /** - * Simple function that returns a prototype ("class"), generated from the given - * definition and all methods bound to the provided visibility object + * Simple function that returns a prototype ("class"), generated from the + * given definition and all methods bound to the provided visibility object */ function createClass( vis, dfn ) { @@ -1321,8 +1359,8 @@ function createClass( vis, dfn ) for ( name in dfn ) { - // ignore any members that are not part of our object (further down the - // chain) + // ignore any members that are not part of our object (further down + // the chain) if ( hasOwn.call( dfn, name ) === false ) { continue; @@ -1335,8 +1373,8 @@ function createClass( vis, dfn ) continue; } - // enclose name in a closure to preserve it (otherwise it'll contain the - // name of the last member in the loop) + // enclose name in a closure to preserve it (otherwise it'll contain + // the name of the last member in the loop) C.prototype[ name ] = ( function( mname ) { return function() @@ -1378,176 +1416,181 @@ Each method call, unless optimized away by the engine, is equivalent to two function invocations, which cuts down on the available stack space. @itemize @item - The method wrapping may complicate tail call optimization, depending on the - JavaScript engine's implementation and whether or not it will optimize across - the stack, rather than just a single-depth recursive call. + The method wrapping may complicate tail call optimization, depending on + the JavaScript engine's implementation and whether or not it will optimize + across the stack, rather than just a single-depth recursive call. @item - As such, for operations that are highly dependent on stack space, one may wish - to avoid method calls and call functions directly. + As such, for operations that are highly dependent on stack space, one may + wish to avoid method calls and call functions directly. @end itemize @item -There is a very slight performance hit (though worrying about this is likely to -be a micro-optimization in the majority of circumstances). +There is a very slight performance hit (though worrying about this is likely +to be a micro-optimization in the majority of circumstances). @end itemize -As mentioned previously, each visibility object is indexed by class identifier -(@pxref{Visibility Object Implementation}). The appropriate visibility object is -bound dynamically on method invocation based on the matching class identifier. -Previously in this discussion, it was not clear how this identifier was -determined at runtime. Since methods are shared by reference between subtypes, -we cannot store a class identifier on the function itself. +As mentioned previously, each visibility object is indexed by class +identifier (@pxref{Visibility Object Implementation}). The appropriate +visibility object is bound dynamically on method invocation based on the +matching class identifier. Previously in this discussion, it was not clear +how this identifier was determined at runtime. Since methods are shared by +reference between subtypes, we cannot store a class identifier on the +function itself. The closure that wraps the actual method references the arguments that were -passed to the function that created it when the class was defined. Among these -arguments are the class identifier and a lookup method used to determine the -appropriate visibility object to use for binding.@footnote{See +passed to the function that created it when the class was defined. Among +these arguments are the class identifier and a lookup method used to +determine the appropriate visibility object to use for binding.@footnote{See @file{lib/MethodWrappers.js} for the method wrappers and -@code{ClassBuilder.getMethodInstance()} for the lookup function.} Therefore, the -wrapper closure will always know the appropriate class identifier. The lookup -method is also passed @var{this}, which is bound to the instance automatically -by JavaScript for the method call. It is on this object that the visibility -objects are stored (non-enumerable; @pxref{Instance Memory Considerations}), -indexed by class identifier. The appropriate is simply returned. +@code{ClassBuilder.getMethodInstance()} for the lookup function.} Therefore, +the wrapper closure will always know the appropriate class identifier. The +lookup method is also passed @var{this}, which is bound to the instance +automatically by JavaScript for the method call. It is on this object that +the visibility objects are stored (non-enumerable; @pxref{Instance Memory +Considerations}), indexed by class identifier. The appropriate is simply +returned. If no visibility object is found, @code{null} is returned by the lookup function, which causes the wrapper function to default to @var{this} as -determined by JavaScript, which will be the instance that the method was invoked -on, or whatever was bound to the function via a call to @code{call()} or -@code{apply()}. This means that, currently, a visibility object can be -explicitly specified for any method by invoking the method in the form of: -@samp{inst.methodName.apply( visobj, arguments )}, which is consistent with how -JavaScript is commonly used with other prototypes. However, it should be noted -that this behavior is undocumented and subject to change in future releases -unless it is decided that this implementation is ideal. It is therefore -recommended to avoid using this functionality for the time being.@footnote{One -one hand, keeping this feature is excellent in the sense that it is predictable. -If all other prototypes work this way, why not ``classes'' as created through -ease.js? At the same time, this is not very class-like. It permits manipulating -the internal state of the class, which is supposed to be encapsulated. It also -allows bypassing constructor logic and replacing methods at runtime. This is -useful for mocking, but a complete anti-pattern in terms of Classical -Object-Oriented development.} +determined by JavaScript, which will be the instance that the method was +invoked on, or whatever was bound to the function via a call to +@code{call()} or @code{apply()}. This means that, currently, a visibility +object can be explicitly specified for any method by invoking the method in +the form of: @samp{inst.methodName.apply( visobj, arguments )}, which is +consistent with how JavaScript is commonly used with other prototypes. +However, it should be noted that this behavior is undocumented and subject +to change in future releases unless it is decided that this implementation +is ideal. It is therefore recommended to avoid using this functionality for +the time being.@footnote{One one hand, keeping this feature is excellent in +the sense that it is predictable. If all other prototypes work this way, +why not ``classes'' as created through ease.js? At the same time, this is +not very class-like. It permits manipulating the internal state of the +class, which is supposed to be encapsulated. It also allows bypassing +constructor logic and replacing methods at runtime. This is useful for +mocking, but a complete anti-pattern in terms of Classical Object-Oriented +development.} @node Pre-ES5 Fallback @subsection Pre-ES5 Fallback For any system that is to remain functionally compatible across a number of -environments, one must develop around the one with the least set of features. In -the case of ease.js, this means designing around the fact that it must maintain -support for older, often unsupported, environments.@footnote{ease.js was -originally developed for use in software that would have to maintain -compatibility as far back as IE6, while still operating on modern web browsers -and within a server-side environment.} The line is drawn between ECMAScript 5 -and its predecessors. +environments, one must develop around the one with the least set of +features. In the case of ease.js, this means designing around the fact that +it must maintain support for older, often unsupported, +environments.@footnote{ease.js was originally developed for use in software +that would have to maintain compatibility as far back as IE6, while still +operating on modern web browsers and within a server-side environment.} The +line is drawn between ECMAScript 5 and its predecessors. As mentioned when describing the proxy implementation (@pxref{Property Proxies}), ease.js's ability to create a framework that is unobtrusive and -fairly easy to work with is attributed to features introduced in ECMAScript 5, -primarily getters and setters. Without them, we cannot proxy between the +fairly easy to work with is attributed to features introduced in ECMAScript +5, primarily getters and setters. Without them, we cannot proxy between the different visibility layers (@pxref{Visibility Object Implementation}). As a consequence, @emph{we cannot use visibility layers within a pre-ES5 environment}. -This brings about the subject of graceful feature degradation. How do we fall -back while still allowing ease.js to operate the same in both environments? +This brings about the subject of graceful feature degradation. How do we +fall back while still allowing ease.js to operate the same in both +environments? @itemize @item Because getters/setters are unsupported, we cannot proxy (@pxref{Property -Proxies}) between visibility layers (@pxref{Visibility Object Implementation}). +Proxies}) between visibility layers (@pxref{Visibility Object +Implementation}). @itemize @item - Visibility support is enforced for development, but it is not necessary in a - production environment (unless that environment makes heavy use of 3rd party - libraries that may abuse the absence of the feature). + Visibility support is enforced for development, but it is not necessary in + a production environment (unless that environment makes heavy use of 3rd + party libraries that may abuse the absence of the feature). @itemize @item Therefore, the feature can be safely dropped. @item - It is important that the developer develops the software in an ECMAScript 5+ - environment to ensure that the visibility constraints are properly enforced. - The developer may then rest assured that their code will work properly in - pre-ES5 environments (so long as they are not using ES5 features in their - own code). + It is important that the developer develops the software in an + ECMAScript 5+ environment to ensure that the visibility constraints + are properly enforced. The developer may then rest assured that their + code will work properly in pre-ES5 environments (so long as they are + not using ES5 features in their own code). @end itemize @end itemize @end itemize @subsubsection Visibility Fallback -Visibility fallback is handled fairly simply in ease.js polymorphically with the -@code{FallbackVisibilityObjectFactory} prototype (as opposed to -@code{VisibilityObjectFactory} which is used in ES5+ environments), which does -the following: +Visibility fallback is handled fairly simply in ease.js polymorphically with +the @code{FallbackVisibilityObjectFactory} prototype (as opposed to +@code{VisibilityObjectFactory} which is used in ES5+ environments), which +does the following: @itemize @item -Property proxies are unsupported. As such, rather than returning a proxy object, -@code{createPropProxy()} will simply return the object that was originally -passed to it. +Property proxies are unsupported. As such, rather than returning a proxy +object, @code{createPropProxy()} will simply return the object that was +originally passed to it. @item This will ultimately result in each layer (public, protected and private) -referencing the same object (the class prototype, also known as the ``public'' -layer). +referencing the same object (the class prototype, also known as the +``public'' layer). @itemize @item - Consequently, all members will be public, just as they would have been without - visibility constraints. + Consequently, all members will be public, just as they would have been + without visibility constraints. @end itemize @end itemize -Classical Object-Oriented programming has many rich features, but many of its -``features'' are simply restrictions it places on developers. This simple fact -works to our benefit. However, in this case of a visibility implementation, we -aren't dealing only with restrictions. There is one exception. +Classical Object-Oriented programming has many rich features, but many of +its ``features'' are simply restrictions it places on developers. This +simple fact works to our benefit. However, in this case of a visibility +implementation, we aren't dealing only with restrictions. There is one +exception. Unfortunately, this necessary fallback introduces a startling limitation: -Consider what might happen if a subtype defines a private member with the same -name as the supertype. Generally, this is not an issue. Subtypes have no -knowledge of supertypes' private members, so there is no potential for conflict. -Indeed, this is the case with our visibility implementation (@pxref{Visibility -Object Implementation}. Unfortunately, if we merge all those layers into one, -we introduce a potential for conflict. +Consider what might happen if a subtype defines a private member with the +same name as the supertype. Generally, this is not an issue. Subtypes have +no knowledge of supertypes' private members, so there is no potential for +conflict. Indeed, this is the case with our visibility implementation +(@pxref{Visibility Object Implementation}. Unfortunately, if we merge all +those layers into one, we introduce a potential for conflict. @anchor{Private Member Dilemma} @subsubsection Private Member Dilemma -With public and protected members (@pxref{Access Modifiers}), we don't have to -worry about conflicts because they are inherited by subtypes -(@pxref{Inheritance}). Private members are intended to remain distinct from any -supertypes; only that specific class has access to its own private members. As -such, inheritance cannot be permitted. However, by placing all values in the -prototype chain (the public layer), we are permitting inheritance of every -member. Under this circumstance, if a subtype were to define a member of the -same name as a supertype, it would effectively be altering the value of its -supertype. Furthermore, the supertype would have access to the same member, -allowing it to modify the values of its @emph{subtypes}, which does not make -sense at all! +With public and protected members (@pxref{Access Modifiers}), we don't have +to worry about conflicts because they are inherited by subtypes +(@pxref{Inheritance}). Private members are intended to remain distinct from +any supertypes; only that specific class has access to its own private +members. As such, inheritance cannot be permitted. However, by placing all +values in the prototype chain (the public layer), we are permitting +inheritance of every member. Under this circumstance, if a subtype were to +define a member of the same name as a supertype, it would effectively be +altering the value of its supertype. Furthermore, the supertype would have +access to the same member, allowing it to modify the values of its +@emph{subtypes}, which does not make sense at all! -This means that we have to place a certain restriction on ease.js as a whole; we -must prevent private member name conflicts even though they cannot occur in ES5 -environments. This is unfortunate, but necessary in order to ensure feature -compatibility across the board. This also has the consequence of allowing the -system to fall back purely for performance benefits (no overhead of the -visibility object). +This means that we have to place a certain restriction on ease.js as a +whole; we must prevent private member name conflicts even though they cannot +occur in ES5 environments. This is unfortunate, but necessary in order to +ensure feature compatibility across the board. This also has the consequence +of allowing the system to fall back purely for performance benefits (no +overhead of the visibility object). @subsubsection Forefitting Fallbacks Although ease.js allows flexibility in what environment one develops for, a developer may choose to support only ES5+ environments and make use of ES5 -features. At this point, the developer may grow frustrated with ease.js limiting -its implementation for pre-ES5 environments when their code will not even run in -a pre-ES5 environment. +features. At this point, the developer may grow frustrated with ease.js +limiting its implementation for pre-ES5 environments when their code will +not even run in a pre-ES5 environment. -For this reason, ease.js may include a feature in the future to disable these -limitations on a class-by-class@footnote{Will also include traits in the -future.} basis in order to provide additional syntax benefits, such as omission -of the static access modifiers (@pxref{Static Implementation}) and removal of -the private member conflict check. +For this reason, ease.js may include a feature in the future to disable +these limitations on a class-by-class@footnote{Will also include traits in +the future.} basis in order to provide additional syntax benefits, such as +omission of the static access modifiers (@pxref{Static Implementation}) and +removal of the private member conflict check. @node Internal Methods/Objects @section Internal Methods/Objects -There are a number of internal methods/objects that may be useful to developers -who are looking to use some features of ease.js without using the full class -system. An API will be provided to many of these in the future, once refactoring -is complete. Until that time, it is not recommended that you rely on any of the -functionality that is not provided via the public API (@code{index.js} or the -global @var{easejs} object). - +There are a number of internal methods/objects that may be useful to +developers who are looking to use some features of ease.js without using the +full class system. An API will be provided to many of these in the future, +once refactoring is complete. Until that time, it is not recommended that +you rely on any of the functionality that is not provided via the public API +(@code{index.js} or the global @var{easejs} object). diff --git a/doc/integration.texi b/doc/integration.texi index 8c961fd..acb1a1a 100644 --- a/doc/integration.texi +++ b/doc/integration.texi @@ -1,11 +1,11 @@ @c This document is part of the GNU ease.js manual. @c Copyright (C) 2011, 2013, 2014 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''. +@c under the terms of the GNU Free Documentation License, Version 1.3 or +@c any later version published by the Free Software Foundation; with no +@c Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. +@c A copy of the license is included in the section entitled ``GNU Free +@c Documentation License''. @node Integration @chapter Integrating GNU ease.js @@ -27,14 +27,15 @@ may not have to build ease.js at all. @section Getting GNU ease.js If you simply want to use ease.js in your project, you may be interested in simply grabbing an archive (tarball, zip, etc), or installing through your -favorite package manger. More information on those options will become available -as ease.js nears its first release. +favorite package manger. More information on those options will become +available as ease.js nears its first release. -If you are interested in building ease.js, you need to get a hold of the source -tree. Either download an archive (tarball, zip, etc), or clone the Git -repository. We will do the latter in the example below. Feel free to clone from -your favorite source. +If you are interested in building ease.js, you need to get a hold of the +source tree. Either download an archive (tarball, zip, etc), or clone the +Git repository. We will do the latter in the example below. Feel free to +clone from your favorite source. +@c TODO: Savannah @example # to clone from GitHub (do one or the other, not both) $ git clone git://github.com/mikegerwitz/easejs @@ -52,11 +53,11 @@ Feel free to skip this section if you have no interest in building ease.js yourself. The build process is fast, and is unnecessary if using ease.js server-side. -First, we should clarify what the term ``build'' means in context of ease.js. -JavaScript is compiled on the fly. That is, we don't actually need to compile it -manually through a build process. So when we are talking about ``building'' -ease.js, we are not talking about compiling the source code. Rather, we are -referring to any of the following: +First, we should clarify what the term ``build'' means in context of +ease.js. JavaScript is compiled on the fly. That is, we don't actually need +to compile it manually through a build process. So when we are talking about +``building'' ease.js, we are not talking about compiling the source code. +Rather, we are referring to any of the following: @itemize @item @@ -66,49 +67,53 @@ Prepare the script for client-side deployment [and testing] Generate the documentation (manual and API) @end itemize -In fact, if you're using ease.js server-side with software such as Node.js, you -do not need to build anything at all. You can simply begin using it. +In fact, if you're using ease.js server-side with software such as Node.js, +you do not need to build anything at all. You can simply begin using it. -The aforementioned are built using @command{make}. The process that is run will -vary depending on your system. The command will read @file{Makefile} in the root -directory and execute the associated command. The following are the targets -available to you: +The aforementioned are built using @command{make}. The process that is run +will vary depending on your system. The command will read @file{Makefile} in +the root directory and execute the associated command. The following are the +targets available to you: @table @command @item mkbuild -Creates the @file{build/} directory, where all output will be stored. This is run -automatically by any of the targets. +Creates the @file{build/} directory, where all output will be stored. This +is run automatically by any of the targets. @item combine -Runs the @command{combine} @ref{Tools Directory, tool} to produce two separate -files: @file{ease.js}, which can be used to use ease.js within the web browser, -and @file{ease-full.js}, which permits both using ease.js and running the unit -tests within the browser. The output is stored in the @file{build/} directory. +Runs the @command{combine} @ref{Tools Directory, tool} to produce two +separate files: @file{ease.js}, which can be used to use ease.js within the +web browser, and @file{ease-full.js}, which permits both using ease.js and +running the unit tests within the browser. The output is stored in the +@file{build/} directory. -The unit tests can be run by opening the @file{build/browser-test.html} file in your -web browser. +The unit tests can be run by opening the @file{build/browser-test.html} file +in your web browser. @item min -Runs @command{combine} and minifies the resulting combined files. These files -are output in the @file{build/} directory and are useful for distribution. It is -recommended that you use the minified files in production. +Runs @command{combine} and minifies the resulting combined files. These +files are output in the @file{build/} directory and are useful for +distribution. It is recommended that you use the minified files in +production. @item test -Run unit tests. This will first perform the @command{combine} process and will -also run the tests for the combined script, ensuring that it was properly -combined. +Run unit tests. This will first perform the @command{combine} process and +will also run the tests for the combined script, ensuring that it was +properly combined. Unit tests will be covered later in the chapter. @item doc -Generates documentation. Currently, only the manual is build. API documentation -will be added in the near future. The resulting documentation will be stored in -@file{build/doc/}. For your convenience, the manual is output in the following -forms: PDF, HTML (single page), HTML (multiple pages) and plain text. +Generates documentation. Currently, only the manual is build. API +documentation will be added in the near future. The resulting documentation +will be stored in @file{build/doc/}. For your convenience, the manual is +output in the following forms: PDF, HTML (single page), HTML (multiple +pages) and plain text. -In order to build the documentation, you must have Texinfo installed. You likely -also need LaTeX installed. If you are on a Debian-based system, for example, you -will likely be able to run the following command to get started: +In order to build the documentation, you must have Texinfo installed. You +likely also need LaTeX installed. If you are on a Debian-based system, for +example, you will likely be able to run the following command to get +started: @example $ sudo apt-get install texlive texinfo @@ -116,8 +121,8 @@ will likely be able to run the following command to get started: @item install Installs info documentation. Must first build @command{doc-info}. After -installation, the manual may be viewed from the command line with: @samp{info -easejs}. +installation, the manual may be viewed from the command line with: +@samp{info easejs}. @item uninstall Removes everything from the system that was installed with @command{make @@ -130,15 +135,15 @@ Runs all targets, except for clean, install and uninstall. Cleans up after the build process by removing the @file{build/} directory. @end table -If you do not want to build ease.js yourself, you are welcome to download the -pre-built files. +If you do not want to build ease.js yourself, you are welcome to download +the pre-built files. @node Including @section Including GNU ease.js In Your Projects -Using ease.js in your projects should be quick and painless. We'll worry about -the details of how to actually @emph{use} ease.js in a bit. For now, let's just -worry about how to include it in your project. +Using ease.js in your projects should be quick and painless. We'll worry +about the details of how to actually @emph{use} ease.js in a bit. For now, +let's just worry about how to include it in your project. @menu * Server-Side Include:: Including ease.js server-side @@ -147,12 +152,13 @@ worry about how to include it in your project. @node Server-Side Include @subsection Server-Side Include -ease.js should work with any CommonJS-compliant system. The examples below have -been tested with Node.js. Support is not guaranteed with any other software. +ease.js should work with any CommonJS-compliant system. The examples below +have been tested with Node.js. Support is not guaranteed with any other +software. Let's assume that you have installed ease.js somewhere that is accessible to -@code{require.paths}. If you used a tool such as @command{npm}, this should have -been done for you. +@code{require.paths}. If you used a tool such as @command{npm}, this should +have been done for you. @float Figure, f:inc-serverside @verbatim @@ -163,22 +169,24 @@ var easejs = require( 'easejs' ); @end float It's important to understand what exactly the above command is doing. We are -including the @file{easejs/} directory (adjust your path as needed). Inside that -directory is the @file{index.js} file, which is loaded. The exports of that -module are returned and assigned to the @var{easejs} variable. We will discuss -what to actually do with those exports later on. +including the @file{easejs/} directory (adjust your path as needed). Inside +that directory is the @file{index.js} file, which is loaded. The exports of +that module are returned and assigned to the @var{easejs} variable. We will +discuss what to actually do with those exports later on. That's it. You should now have ease.js available to your project. @node Client-Side Include @subsection Client-Side Include (Web Browser) -ease.js can also be included in the web browser. Not only does this give you a -powerful Object-Oriented framework client-side, but it also facilitates code -reuse by permitting you to reuse your server-side code that depends on ease.js. +ease.js can also be included in the web browser. Not only does this give you +a powerful Object-Oriented framework client-side, but it also facilitates +code reuse by permitting you to reuse your server-side code that depends on +ease.js. In order for ease.js to operate within the client, you must either download -@file{ease.js} or @ref{Building, build it yourself}. Let's assume that you have -placed @file{ease.js} within the @file{scripts/} directory of your web root. +@file{ease.js} or @ref{Building, build it yourself}. Let's assume that you +have placed @file{ease.js} within the @file{scripts/} directory of your web +root. @float Figure, f:inc-clientside @verbatim @@ -191,10 +199,10 @@ placed @file{ease.js} within the @file{scripts/} directory of your web root. @caption{Including ease.js client-side} @end float -Likely, you only want the first one. The unit tests can more easily be run by -loading @file{build/browser-test.html} in your web browser (@pxref{Building}). - -The script will define a global @var{easejs} variable, which can be used exactly -like the server-side @code{require()} (@pxref{Server-Side Include}). Keep that -in mind when going through the examples in this manual. +Likely, you only want the first one. The unit tests can more easily be run +by loading @file{build/browser-test.html} in your web browser +(@pxref{Building}). +The script will define a global @var{easejs} variable, which can be used +exactly like the server-side @code{require()} (@pxref{Server-Side Include}). +Keep that in mind when going through the examples in this manual. diff --git a/doc/interactive.js b/doc/interactive.js index 8b35d7c..2f9a56d 100644 --- a/doc/interactive.js +++ b/doc/interactive.js @@ -39,12 +39,15 @@ jqueryCheck( function() $( document ).ready( function() { // syntax highlighting for code samples - $( '.verbatim, .samp, .code, .example' ).each( function( i, element ) - { - hljs.highlightBlock( element, ' ' ); - } ); + $( '.verbatim, .samp, .code, .example' ).each( + function( i, element ) + { + hljs.highlightBlock( element, ' ' ); + } + ); - // quick-n-dirty sub and super script impl (it is by no means perfect) + // quick-n-dirty sub and super script impl (it is by no means + // perfect) $( 'var:contains("\\")' ).each( function() { var $this = $( this ); diff --git a/doc/license.texi b/doc/license.texi index d0388fe..fb8ffb3 100644 --- a/doc/license.texi +++ b/doc/license.texi @@ -9,8 +9,8 @@ @c hence no sectioning command or @node. @display -Copyright @copyright{} 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. -@uref{http://fsf.org/} +Copyright @copyright{} 2000, 2001, 2002, 2007, 2008 Free Software +Foundation, Inc. @uref{http://fsf.org/} Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -122,9 +122,9 @@ A section ``Entitled XYZ'' means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as ``Acknowledgements'', -``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' -of such a section when you modify the Document means that it remains a -section ``Entitled XYZ'' according to this definition. +``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the +Title'' of such a section when you modify the Document means that it remains +a section ``Entitled XYZ'' according to this definition. The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty diff --git a/doc/mkeywords.texi b/doc/mkeywords.texi index 3fe70aa..6813c0b 100644 --- a/doc/mkeywords.texi +++ b/doc/mkeywords.texi @@ -1,21 +1,22 @@ @c This document is part of the GNU ease.js manual. @c Copyright (C) 2011, 2013, 2014 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''. +@c under the terms of the GNU Free Documentation License, Version 1.3 or +@c any later version published by the Free Software Foundation; with no +@c Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. +@c A copy of the license is included in the section entitled ``GNU Free +@c Documentation License''. @node Member Keywords @chapter Member Keywords Keywords are defined within the context of the @dfn{definition object} -(@pxref{dfnobj,,Definition Object}). In the sections that follow, let @var{C} -denote a class that contains the definition object @var{dfn}, which in turn -contains @var{keywords} within the declaration of method @var{name}, whose -definition is denoted by @var{value}. +(@pxref{dfnobj,,Definition Object}). In the sections that follow, let +@var{C} denote a class that contains the definition object @var{dfn}, which +in turn contains @var{keywords} within the declaration of method @var{name}, +whose definition is denoted by @var{value}. -The table below summarizes the available keywords accepted by @var{keywords}. +The table below summarizes the available keywords accepted by +@var{keywords}. @float Table, t:keywords @multitable @columnfractions .10 .90 @@ -24,29 +25,31 @@ The table below summarizes the available keywords accepted by @var{keywords}. @tab Places member @var{name} into the public API for @var{C} (@pxref{Access Modifiers}). @item @code{protected} -@tab Places member @var{name} into the protected API for @var{C} (@pxref{Access -Modifiers}). +@tab Places member @var{name} into the protected API for @var{C} +(@pxref{Access Modifiers}). @item @code{private} -@tab Places member @var{name} into the private API for @var{C} (@pxref{Access -Modifiers}). +@tab Places member @var{name} into the private API for @var{C} +(@pxref{Access Modifiers}). @item @code{static} -@tab Binds member @var{name} to class @var{C} rather than instance of @var{C}. -Member data shared with each instance of type @var{C}. @xref{Static Members}. +@tab Binds member @var{name} to class @var{C} rather than instance of +@var{C}. Member data shared with each instance of type @var{C}. +@xref{Static Members}. @item @code{abstract} -@tab Declares member @var{name} and defers definition to subtype. @var{value} -is interpreted as an argument list and must be of type @code{array}. May only be -used with methods. Member @var{name} must be part of @var{dfn} of either an +@tab Declares member @var{name} and defers definition to subtype. +@var{value} is interpreted as an argument list and must be of type +@code{array}. May only be used with methods. Member @var{name} must be part +of @var{dfn} of either an @code{Interface} or @code{AbstractClass}. @xref{Abstract Members}. @item @code{const} -@tab Defines an immutable property @var{name}. May not be used with methods or -getters/setters. @xref{Constants}. +@tab Defines an immutable property @var{name}. May not be used with methods +or getters/setters. @xref{Constants}. @item @code{virtual} -@tab Declares that method @var{name} may be overridden by subtypes. Methods +@tab Declares that method @var{name} may be overridden by subtypes. Methods without this keyword may not be overridden. May only be used with methods. @xref{Inheritance}. @item @code{override} -@tab Overrides method @var{name} of supertype of @var{C} with @var{value}. May -only override virtual methods. May only be used with methods. +@tab Overrides method @var{name} of supertype of @var{C} with @var{value}. +May only override virtual methods. May only be used with methods. @xref{Inheritance}. @item @code{proxy} @tab Proxies calls to method @var{name} to the object stored in property @@ -55,9 +58,9 @@ only override virtual methods. May only be used with methods. @caption{Supported keywords} @end float -Not all keywords are supported by each member and some keywords conflict with -each other. More information can be found in the appropriate sections as -mentioned above in @ref{t:keywords}. +Not all keywords are supported by each member and some keywords conflict +with each other. More information can be found in the appropriate sections +as mentioned above in @ref{t:keywords}. @menu * Access Modifiers:: Control the context in which members may be accessed @@ -67,8 +70,9 @@ mentioned above in @ref{t:keywords}. @section Access Modifiers @dfn{Access modifiers}, when provided in @var{keywords}, alter the interface into which the definition of member @var{name} is placed. There are three -interfaces, or levels of @dfn{visibility}, that dictate the context from which a -member may be accessed, listed here from the most permissive to the least: +interfaces, or levels of @dfn{visibility}, that dictate the context from +which a member may be accessed, listed here from the most permissive to the +least: @table @dfn @item public @@ -91,17 +95,17 @@ Not accessible outside of @var{C} or any instance of @var{C}. @emph{Not} @headitem Keyword @tab Description @item @code{public} @tab -Places member @var{name} in public interface (accessible outside of @var{C} or -instance of @var{C}; accessible by subtypes). Implied if no other access +Places member @var{name} in public interface (accessible outside of @var{C} +or instance of @var{C}; accessible by subtypes). Implied if no other access modifier is provided. @item @code{protected} @tab -Places member @var{name} in protected interface (accessible only within @var{C} -or instance of @var{C}; accessible by subtypes). +Places member @var{name} in protected interface (accessible only within +@var{C} or instance of @var{C}; accessible by subtypes). @item @code{private} @tab -Places member @var{name} in private interface (accessible only within @var{C} or -instance of @var{C}; not accessible by subtypes). +Places member @var{name} in private interface (accessible only within +@var{C} or instance of @var{C}; not accessible by subtypes). @end multitable @caption{Access modifiers} @end float @@ -110,10 +114,11 @@ Access modifiers have the following properties: @itemize @item -Only one access modifier may appear in @var{keywords} for any given @var{name}. +Only one access modifier may appear in @var{keywords} for any given +@var{name}. @item -If no access modifier is provided in @var{keywords} for any member @var{name}, -member @var{name} is implicitly @code{public}. +If no access modifier is provided in @var{keywords} for any member +@var{name}, member @var{name} is implicitly @code{public}. @end itemize @menu @@ -125,87 +130,92 @@ member @var{name} is implicitly @code{public}. @subsection Discussion One of the major hurdles ease.js aimed to address (indeed, one of the core reasons for its creation) was that of encapsulation. JavaScript's prototypal -model provides limited means of encapsulating data. Since functions limit scope, -they may be used to mimic private members; these are often referred to as -@dfn{privileged members}. However, declaring classes in this manner tends be -messy, which has the consequence of increasing maintenance costs and reducing -the benefit of the implementation. ease.js aims to provide an elegant -implementation that is both a pleasure to work with and able to support -protected members. +model provides limited means of encapsulating data. Since functions limit +scope, they may be used to mimic private members; these are often referred +to as @dfn{privileged members}. However, declaring classes in this manner +tends be messy, which has the consequence of increasing maintenance costs +and reducing the benefit of the implementation. ease.js aims to provide an +elegant implementation that is both a pleasure to work with and able to +support protected members. -By default, all members are public. This means that the members can be accessed -and modified from within an instance as well as from outside of it. Subtypes -(classes that inherit from it; @pxref{Inheritance}) will inherit public members. -Public methods expose an API by which users may use your class. Public -properties, however, should be less common in practice for a very important -reason, which is explored throughout the remainder of this section. +By default, all members are public. This means that the members can be +accessed and modified from within an instance as well as from outside of it. +Subtypes (classes that inherit from it; @pxref{Inheritance}) will inherit +public members. Public methods expose an API by which users may use your +class. Public properties, however, should be less common in practice for a +very important reason, which is explored throughout the remainder of this +section. @anchor{Encapsulation} @subsubsection Encapsulation -@dfn{Encapsulation} is the act of hiding information within a class or instance. -Classes should be thought of black boxes; we want them to do their job, but we -should not concern ourselves with @emph{how} they do their job. Encapsulation -takes a great deal of complexity out of an implementation and allows the -developer to focus on accomplishing the task by focusing on the implementing in -terms of the problem domain. +@dfn{Encapsulation} is the act of hiding information within a class or +instance. Classes should be thought of black boxes; we want them to do +their job, but we should not concern ourselves with @emph{how} they do their +job. Encapsulation takes a great deal of complexity out of an implementation +and allows the developer to focus on accomplishing the task by focusing on +the implementing in terms of the problem domain. -For example - consider a class named @var{Dog} which has a method @code{walk()}. -To walk a dog, we simply call @code{Dog().walk()}. The @code{walk()} method -could be doing anything. In the case of a real dog, perhaps it will send a -message to the dog's brain, perform the necessary processing to determine how -that command should be handled and communicate the result to the limbs. The -limbs will communicate back the information they receive from their nerves, -which will be processed by the brain to determine when they hit the ground, -thereby triggering additional actions and the further movement of the other -legs. This could be a terribly complicated implementation if we had to worry -about how all of this was done. +For example - consider a class named @var{Dog} which has a method +@code{walk()}. To walk a dog, we simply call @code{Dog().walk()}. The +@code{walk()} method could be doing anything. In the case of a real dog, +perhaps it will send a message to the dog's brain, perform the necessary +processing to determine how that command should be handled and communicate +the result to the limbs. The limbs will communicate back the information +they receive from their nerves, which will be processed by the brain to +determine when they hit the ground, thereby triggering additional actions +and the further movement of the other legs. This could be a terribly +complicated implementation if we had to worry about how all of this was +done. -In addition to the actual walking algorithm, we have the state of each of the -legs - their current position, their velocity, the state of each of the muscles, -etc. This state pertains only to the operations performed by the dog. Exposing -this state to everyone wouldn't be terribly useful. Indeed, if this information -was exposed, it would complicate the implementation. What if someone decided to -alter this state in the middle of a walking operation? Or what if the developer -implementing @var{Dog} relied on this state in order to determine when the leg -reached a certain position, but later versions of @var{Dog} decided to alter the -algorithm, thereby changing those properties? +In addition to the actual walking algorithm, we have the state of each of +the legs - their current position, their velocity, the state of each of the +muscles, etc. This state pertains only to the operations performed by the +dog. Exposing this state to everyone wouldn't be terribly useful. Indeed, if +this information was exposed, it would complicate the implementation. What +if someone decided to alter this state in the middle of a walking operation? +Or what if the developer implementing @var{Dog} relied on this state in +order to determine when the leg reached a certain position, but later +versions of @var{Dog} decided to alter the algorithm, thereby changing those +properties? -By preventing these details from being exposed, we present the developer with a -very simple interface@footnote{One would argue that this isn't necessary a good -thing. What if additional flexibility was needed? @var{Dog}, in the sense of -this example, can be thought of as a Facade (GoF). One could provide more -flexibility by composing @var{Dog} of, say, @var{Leg} instances, a @var{Brain}, -etc. However, encapsulation still remains a factor. Each of those components -would encapsulate their own data.}. Rather than the developer having to be -concerned with moving each of the dog's legs, all they have to do is understand -that the dog is being walked. +By preventing these details from being exposed, we present the developer +with a very simple interface@footnote{One would argue that this isn't +necessary a good thing. What if additional flexibility was needed? +@var{Dog}, in the sense of this example, can be thought of as a Facade +(GoF). One could provide more flexibility by composing @var{Dog} of, say, +@var{Leg} instances, a @var{Brain}, etc. However, encapsulation still +remains a factor. Each of those components would encapsulate their own +data.}. Rather than the developer having to be concerned with moving each of +the dog's legs, all they have to do is understand that the dog is being +walked. When developing your classes, the following best practices should be kept in mind: @itemize @item -When attempting to determine the best access modifier (@pxref{Access Modifiers}) -to use for a member, start with the least level of visibility (@code{private}) -and work your way up if necessary. +When attempting to determine the best access modifier (@pxref{Access +Modifiers}) to use for a member, start with the least level of visibility +(@code{private}) and work your way up if necessary. @item If your member is not private, be sure that you can justify your choice. @itemize @item - If protected - why do subclasses need access to that data? Is there a better - way to accomplish the same task without breaking encapsulation? + If protected - why do subclasses need access to that data? Is there a + better way to accomplish the same task without breaking encapsulation? @item - If public - is this member necessary to use the class externally? In the case - of a method - does it make sense to be part of a public API? If a property - - why is that data not encapsulated? Should you consider an accessor method? + If public - is this member necessary to use the class externally? In the + case of a method - does it make sense to be part of a public API? If a + property - why is that data not encapsulated? Should you consider an + accessor method? @end itemize @end itemize @node Access Modifiers Example @subsection Example -Let's consider our @var{Dog} class in more detail. We will not go so far as to -implement an entire nervous system in our example. Instead, let's think of our -@var{Dog} similar to a wind-up toy: +Let's consider our @var{Dog} class in more detail. We will not go so far as +to implement an entire nervous system in our example. Instead, let's think +of our @var{Dog} similar to a wind-up toy: @float Figure, f:encapsulation @verbatim @@ -257,23 +267,23 @@ Class( 'Dog', @end float As you can see above, the act of making the dog move forward is a bit more -complicated than the developer may have originally expected. The dog has four -separate legs that need to be moved individually. The dog must also first stand -before it can be walked, but it can only stand if it's sitting. Detailed tasks -such as these occur all the time in classes, but they are hidden from the -developer using the public API. The developer should not be concerned with all -of the legs. Worrying about such details brings the developer outside of the -problem domain and into a @emph{new} problem domain - how to get the dog to -walk. +complicated than the developer may have originally expected. The dog has +four separate legs that need to be moved individually. The dog must also +first stand before it can be walked, but it can only stand if it's sitting. +Detailed tasks such as these occur all the time in classes, but they are +hidden from the developer using the public API. The developer should not be +concerned with all of the legs. Worrying about such details brings the +developer outside of the problem domain and into a @emph{new} problem domain +- how to get the dog to walk. @subsection Private Members Let's first explore private members. The majority of the members in the -@var{Dog} class (@pxref{f:encapsulation,}) are private. This is the lowest level -of visibility (and consequently the @emph{highest} level of encapsulation). By -convention, we prefix private members with an underscore. Private members are -available @emph{only to the class that defined it} and are not available outside -the class. +@var{Dog} class (@pxref{f:encapsulation,}) are private. This is the lowest +level of visibility (and consequently the @emph{highest} level of +encapsulation). By convention, we prefix private members with an underscore. +Private members are available @emph{only to the class that defined it} and +are not available outside the class. @float Figure, f:encapsulation-call-priv @verbatim @@ -286,16 +296,18 @@ the class. @end float You will notice that the dog's legs are declared private as well -(@pxref{f:encapsulation,}). This is to ensure we look at the dog as a whole; we -don't care about what the dog is made up of. Legs, fur, tail, teeth, tongue, etc +(@pxref{f:encapsulation,}). This is to ensure we look at the dog as a whole; +we don't care about what the dog is made up of. Legs, fur, tail, teeth, +tongue, etc - they are all irrelevant to our purpose. We just want to walk the dog. -Encapsulating those details also ensures that they will not be tampered with, -which will keep the dog in a consistent, predictable state. + Encapsulating those details also ensures that they will not be tampered + with, which will keep the dog in a consistent, predictable state. -Private members cannot be inherited. Let's say we want to make a class called -@var{TwoLeggedDog} to represent a dog that was trained to walk only on two feet. -We could approach this in a couple different ways. The first way would be to -prevent the front legs from moving. What happens when we explore that approach: +Private members cannot be inherited. Let's say we want to make a class +called @var{TwoLeggedDog} to represent a dog that was trained to walk only +on two feet. We could approach this in a couple different ways. The first +way would be to prevent the front legs from moving. What happens when we +explore that approach: @float Figure, f:encapsulation-inherit-priv @@ -318,22 +330,22 @@ prevent the front legs from moving. What happens when we explore that approach: @end float If you were to attempt to walk a @var{TwoLeggedDog}, you would find that -@emph{the dog's front legs still move}! This is because, as mentioned before, -private methods are not inherited. Rather than overriding the parent's -@var{_moveFrontLeg} method, you are instead @emph{defining a new method}, with -the name @var{_moveFrontLeg}. The old method will still be called. Instead, we -would have to override the public @var{walk} method to prevent our dog from -moving his front feet. +@emph{the dog's front legs still move}! This is because, as mentioned +before, private methods are not inherited. Rather than overriding the +parent's @var{_moveFrontLeg} method, you are instead @emph{defining a new +method}, with the name @var{_moveFrontLeg}. The old method will still be +called. Instead, we would have to override the public @var{walk} method to +prevent our dog from moving his front feet. @subsection Protected Members Protected members are often misunderstood. Many developers will declare all -of their members as either public or protected under the misconception that they -may as well allow subclasses to override whatever functionality they want. This -makes the class more flexible. +of their members as either public or protected under the misconception that +they may as well allow subclasses to override whatever functionality they +want. This makes the class more flexible. -While it is true that the class becomes more flexible to work with for subtypes, -this is a dangerous practice. In fact, doing so @emph{violates encapsulation}. -Let's reconsider the levels of visibility in this manner: +While it is true that the class becomes more flexible to work with for +subtypes, this is a dangerous practice. In fact, doing so @emph{violates +encapsulation}. Let's reconsider the levels of visibility in this manner: @table @strong @item public @@ -347,19 +359,20 @@ Provides an API for @emph{the class itself}. @end table Just as we want to hide data from the public API, we want to do the same for -subtypes. If we simply expose all members to any subclass that comes by, that -acts as a peephole in our black box. We don't want people spying into our -internals. Subtypes shouldn't care about the dog's implementation either. +subtypes. If we simply expose all members to any subclass that comes by, +that acts as a peephole in our black box. We don't want people spying into +our internals. Subtypes shouldn't care about the dog's implementation +either. Private members should be used whenever possible, unless you are looking to -provide subtypes with the ability to access or override methods. In that case, -we can move up to try protected members. Remember not to make a member public -unless you wish it to be accessible to the entire world. +provide subtypes with the ability to access or override methods. In that +case, we can move up to try protected members. Remember not to make a +member public unless you wish it to be accessible to the entire world. @var{Dog} (@pxref{f:encapsulation,}) defined a single method as protected - @code{stand()}. Because the method is protected, it can be inherited by -subtypes. Since it is inherited, it may also be overridden. Let's define another -subtype, @var{LazyDog}, which refuses to stand. +subtypes. Since it is inherited, it may also be overridden. Let's define +another subtype, @var{LazyDog}, which refuses to stand. @float Figure, f:encapsulation-inherit-prot @verbatim @@ -381,19 +394,19 @@ subtype, @var{LazyDog}, which refuses to stand. @caption{Protected members are inherited by subtypes} @end float -There are a couple important things to be noted from the above example. Firstly, -we are able to override the @code{walk()} method, because it was inherited. -Secondly, since @code{rollOver()} was also inherited from the parent, we are -able to call that method, resulting in an upside-down dog that refuses to stand -up, just moving his feet. +There are a couple important things to be noted from the above example. +Firstly, we are able to override the @code{walk()} method, because it was +inherited. Secondly, since @code{rollOver()} was also inherited from the +parent, we are able to call that method, resulting in an upside-down dog +that refuses to stand up, just moving his feet. Another important detail to notice is that @code{Dog.rollOver()} accesses a -private property of @var{Dog} -- @var{_body}. Our subclass does not have access -to that variable. Since it is private, it was not inherited. However, since the -@code{rollOver()} method is called within the context of the @var{Dog} class, -the @emph{method} has access to the private member, allowing our dog to -successfully roll over. If, on the other hand, we were to override -@code{rollOver()}, our code would @emph{not} have access to that private object. -Calling @samp{this.__super()} from within the overridden method would, however, -call the parent method, which would again have access to its parent's private -members. +private property of @var{Dog} -- @var{_body}. Our subclass does not have +access to that variable. Since it is private, it was not inherited. However, +since the @code{rollOver()} method is called within the context of the +@var{Dog} class, the @emph{method} has access to the private member, +allowing our dog to successfully roll over. If, on the other hand, we were +to override @code{rollOver()}, our code would @emph{not} have access to that +private object. Calling @samp{this.__super()} from within the overridden +method would, however, call the parent method, which would again have access +to its parent's private members. diff --git a/doc/source-tree.texi b/doc/source-tree.texi index 0203a09..d458c7f 100644 --- a/doc/source-tree.texi +++ b/doc/source-tree.texi @@ -1,11 +1,11 @@ @c This document is part of the GNU ease.js manual. @c Copyright (C) 2011, 2013, 2014 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''. +@c under the terms of the GNU Free Documentation License, Version 1.3 or +@c any later version published by the Free Software Foundation; with no +@c Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. +@c A copy of the license is included in the section entitled ``GNU Free +@c Documentation License''. @node Source Tree @appendix Source Tree @@ -19,7 +19,8 @@ follow along. doc/ lib/ test/ tools/ @end example -The project contains four main directories in addition to the root directory: +The project contains four main directories in addition to the root +directory: @table @file @item ./ @@ -27,8 +28,8 @@ The root directory contains basic project files, such as @file{README}, @file{Makefile} and @file{index.js}. @item doc/ -Contains documentation source files (you are currently reading part of it - the -manual). +Contains documentation source files (you are currently reading part of it - +the manual). @item lib/ Contains the actual source code for the various modules. @@ -65,20 +66,21 @@ Contains the project license. Invoked by the @command{make} command. Used for building ease.js. @item package.json -Used by @command{npm}, a package manager for Node.js, to automate installation. +Used by @command{npm}, a package manager for Node.js, to automate +installation. @item README.hacking Useful information for those looking to modify/contribute to the project. @item README.md Serves as a quick reference for the project, in markdown@footnote{See -@uref{http://en.wikipedia.org/wiki/Markdown}.} format. This format was chosen -because it is displayed nicely on GitHub. +@uref{http://en.wikipedia.org/wiki/Markdown}.} format. This format was +chosen because it is displayed nicely on GitHub. @item README.todo Incomplete tasks. Future direction of the project. If you're looking to help -out, take a look at this file to see what needs to be done. (See also the bug -tracker at @uref{http://easejs.org/bugs}). +out, take a look at this file to see what needs to be done. (See also the +bug tracker at @uref{http://easejs.org/bugs}). @end table These files will be discussed in further detail when they are actually used. @@ -86,51 +88,53 @@ These files will be discussed in further detail when they are actually used. @node Doc Directory @section Doc Directory -The @file{doc/} directory contains the source files for the manual. The source -files are in Texinfo@footnote{See @uref{http://www.gnu.org/software/texinfo/}.} -format. Instructions for compiling the documentation are included later in this -chapter. +The @file{doc/} directory contains the source files for the manual. The +source files are in Texinfo@footnote{See +@uref{http://www.gnu.org/software/texinfo/}.} format. Instructions for +compiling the documentation are included later in this chapter. -API documentation is @emph{not} included in this directory. It is generated from -the source code. +API documentation is @emph{not} included in this directory. It is generated +from the source code. @node Lib Directory @section Lib Directory -The @file{lib/} directory contains the source code for the project. Each source -file represents a single CommonJS module, often containing a prototype, and is -written in JavaScript. Additional information about each of the modules can be -found in the header of each file. +The @file{lib/} directory contains the source code for the project. Each +source file represents a single CommonJS module, often containing a +prototype, and is written in JavaScript. Additional information about each +of the modules can be found in the header of each file. -Unless you are developing for ease.js, you needn't concern yourself with these -files. @file{index.js}, in the root directory, contains mappings to these files -where necessary, exposing the useful portions of the API for general use. You -can use ease.js without even recognizing that the @file{lib/} directory even -exists. +Unless you are developing for ease.js, you needn't concern yourself with +these files. @file{index.js}, in the root directory, contains mappings to +these files where necessary, exposing the useful portions of the API for +general use. You can use ease.js without even recognizing that the +@file{lib/} directory even exists. @node Test Directory @section Test Directory -The @file{test/} directory contains all the unit tests for the project. ease.js -follows a test-driven development model; every single aspect of the framework is -tested to ensure that features work as intended both server-side and across all -supported web browsers. The tests also serve as regression tests, ensuring that -bugs are not introduced for anything that has been covered. These tests should -also give outside developers confidence; if a developer makes a modification to -ease.js and does not cause any failing tests, it's likely that their change -didn't have negative consequences on the integrity of the framework. +The @file{test/} directory contains all the unit tests for the project. +ease.js follows a test-driven development model; every single aspect of the +framework is tested to ensure that features work as intended both +server-side and across all supported web browsers. The tests also serve as +regression tests, ensuring that bugs are not introduced for anything that +has been covered. These tests should also give outside developers +confidence; if a developer makes a modification to ease.js and does not +cause any failing tests, it's likely that their change didn't have negative +consequences on the integrity of the framework. -ease.js is currently in a transition period in regards to the style of the test -cases. Tests written in the original format are prefixed with @samp{test-}, -followed by the name of the module, followed optionally by the specific part of -the module that is being tested. Newer test cases are prefixed with the -prototype name of the unit being tested, followed by @samp{Test.js}. If there -are a number of test cases for a given prototype, any number of tests will be -included (with the same suffix) in a directory with the same name as the -prototype. The tests are written in JavaScript and use Node.js's @file{assert} -module. Newer tests use a test case system that was developed to suit the needs -of the project (still using the @file{assert} module). They may be run -individually or all at once during the build process. +ease.js is currently in a transition period in regards to the style of the +test cases. Tests written in the original format are prefixed with +@samp{test-}, followed by the name of the module, followed optionally by the +specific part of the module that is being tested. Newer test cases are +prefixed with the prototype name of the unit being tested, followed by +@samp{Test.js}. If there are a number of test cases for a given prototype, +any number of tests will be included (with the same suffix) in a directory +with the same name as the prototype. The tests are written in JavaScript and +use Node.js's @file{assert} module. Newer tests use a test case system that +was developed to suit the needs of the project (still using the +@file{assert} module). They may be run individually or all at once during +the build process. Developers interested in contributing to ease.js can aid in this transition process by helping to move all @file{test-*} tests over to the new test case @@ -142,26 +146,27 @@ performance tests used for benchmarking. @node Tools Directory @section Tools Directory -The @file{tools/} directory contains scripts and data necessary for the build -process. The tools are shell scripts that may be run independently of the build -process if you find them to be useful. The remaining files are data to accompany -those tools. +The @file{tools/} directory contains scripts and data necessary for the +build process. The tools are shell scripts that may be run independently of +the build process if you find them to be useful. The remaining files are +data to accompany those tools. @table @file @item combine Concatenates all the modules and wraps them for client-side deployment. If -requested, the tests are also wrapped and concatenated so that they may be run -in the web browser. The contents are stripped of trailing commas using the -@command{rmtrail} tool. The resulting file is @emph{not} minified; the user can -use whatever process he/she wishes to do so. In the future, minification will be -part of the build script. +requested, the tests are also wrapped and concatenated so that they may be +run in the web browser. The contents are stripped of trailing commas using +the @command{rmtrail} tool. The resulting file is @emph{not} minified; the +user can use whatever process he/she wishes to do so. In the future, +minification will be part of the build script. @item rmtrail -Removes trailing commas from object and array definitions. Reads from standard -in. @emph{This script is not intelligent.} It was designed to work with ease.js. -It does not, for example, check to ensure that it is not removing commas from -within strings. This would not be a difficult addition, but is currently -unnecessary. Use caution when using this tool outside of ease.js. +Removes trailing commas from object and array definitions. Reads from +standard in. @emph{This script is not intelligent.} It was designed to work +with ease.js. It does not, for example, check to ensure that it is not +removing commas from within strings. This would not be a difficult addition, +but is currently unnecessary. Use caution when using this tool outside of +ease.js. @item minify.js Responsible for receiving input from stdin and writing minified output to @@ -169,33 +174,31 @@ stdout. This script uses UglifyJS to minify source files for distribution, improving download times. @item browser-test.html -Skeleton page to be used after the build process. Runs ease.js unit tests in the -web browser and reports any failures. This is very important to ensure that -ease.js operates consistently between all supported browsers. The tests that are -run are the same exact tests that are run server-side. +Skeleton page to be used after the build process. Runs ease.js unit tests in +the web browser and reports any failures. This is very important to ensure +that ease.js operates consistently between all supported browsers. The tests +that are run are the same exact tests that are run server-side. @item combine-test.tpl -Contains a client-side implementation of any modules required for testing. This -file contains mainly assertions. It is included by the @command{combine} script -when tests are requested. +Contains a client-side implementation of any modules required for testing. +This file contains mainly assertions. It is included by the +@command{combine} script when tests are requested. @item combine.tpl Contains the basic functionality required to get CommonJS modules working -client-side. This is a very basic implementation, only doing what is necessary -for ease.js to work properly. It is not meant to be a solution for all of your -client-side CommonJS problems. +client-side. This is a very basic implementation, only doing what is +necessary for ease.js to work properly. It is not meant to be a solution for +all of your client-side CommonJS problems. @item license.tpl Contains the license that is to appear atop every combined file, including minified. The original text must remain in tact. If you make changes to the -source code, you are welcome to add additional text. See the @file{LICENSE} file -in the root directory for more information on what is permitted. +source code, you are welcome to add additional text. See the @file{LICENSE} +file in the root directory for more information on what is permitted. @end table While the tools may be useful outside of ease.js in some regard, please note that they have been tailored especially for ease.js. They do not contain -unnecessary features that ease.js does not need to make use of. Therefore, you -may need to adapt them to your own project and individual needs should you -decide to use them in your own projects. - - +unnecessary features that ease.js does not need to make use of. Therefore, +you may need to adapt them to your own project and individual needs should +you decide to use them in your own projects.