commit 74476ca64a8c14cb347a37be07be13a86fce141b Author: Mike Gerwitz Date: Wed Feb 15 23:11:23 2012 -0500 Initial commit of working draft, revised diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bfef807 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.aux +*.log +*.toc +*.dvi +*.pdf +*.ps +*.out diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..2f7e03c --- /dev/null +++ b/COPYING @@ -0,0 +1,451 @@ + + GNU Free Documentation License + Version 1.3, 3 November 2008 + + + Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document "free" in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The "Document", below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as "you". You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A "Modified Version" of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A "Secondary Section" is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall +subject (or to related matters) and contains nothing that could fall +directly within that overall subject. (Thus, if the Document is in +part a textbook of mathematics, a Secondary Section may not explain +any mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The "Invariant Sections" are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The "Cover Texts" are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A "Transparent" copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not "Transparent" is called "Opaque". + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML, PostScript or PDF designed for human modification. Examples of +transparent image formats include PNG, XCF and JPG. Opaque formats +include proprietary formats that can be read and edited only by +proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML, PostScript or PDF produced by some word +processors for output purposes only. + +The "Title Page" means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +The "publisher" means any person or entity that distributes copies of +the Document to the public. + +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. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + +2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no +other conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +3. COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to +give them a chance to provide you with an updated version of the +Document. + + +4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +A. Use in the Title Page (and on the covers, if any) a title distinct + from that of the Document, and from those of previous versions + (which should, if there were any, be listed in the History section + of the Document). You may use the same title as a previous version + if the original publisher of that version gives permission. +B. List on the Title Page, as authors, one or more persons or entities + responsible for authorship of the modifications in the Modified + Version, together with at least five of the principal authors of the + Document (all of its principal authors, if it has fewer than five), + unless they release you from this requirement. +C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. +D. Preserve all the copyright notices of the Document. +E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. +F. Include, immediately after the copyright notices, a license notice + giving the public permission to use the Modified Version under the + terms of this License, in the form shown in the Addendum below. +G. Preserve in that license notice the full lists of Invariant Sections + and required Cover Texts given in the Document's license notice. +H. Include an unaltered copy of this License. +I. Preserve the section Entitled "History", Preserve its Title, and add + to it an item stating at least the title, year, new authors, and + publisher of the Modified Version as given on the Title Page. If + there is no section Entitled "History" in the Document, create one + stating the title, year, authors, and publisher of the Document as + given on its Title Page, then add an item describing the Modified + Version as stated in the previous sentence. +J. Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and likewise + the network locations given in the Document for previous versions + it was based on. These may be placed in the "History" section. + You may omit a network location for a work that was published at + least four years before the Document itself, or if the original + publisher of the version it refers to gives permission. +K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the section all + the substance and tone of each of the contributor acknowledgements + and/or dedications given therein. +L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section titles. +M. Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. +N. Do not retitle any existing section to be Entitled "Endorsements" + or to conflict in title with any Invariant Section. +O. Preserve any Warranty Disclaimers. + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled "History" +in the various original documents, forming one section Entitled +"History"; likewise combine any sections Entitled "Acknowledgements", +and any sections Entitled "Dedications". You must delete all sections +Entitled "Endorsements". + + +6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other +documents released under this License, and replace the individual +copies of this License in the various documents with a single copy +that is included in the collection, provided that you follow the rules +of this License for verbatim copying of each of the documents in all +other respects. + +You may extract a single document from such a collection, and +distribute it individually under this License, provided you insert a +copy of this License into the extracted document, and follow this +License in all other respects regarding verbatim copying of that +document. + + +7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an "aggregate" if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + + +8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled "Acknowledgements", +"Dedications", or "History", the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + + +9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense, or distribute it is void, and +will automatically terminate your rights under this License. + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, receipt of a copy of some or all of the same material does +not give you any rights to use it. + + +10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions of the +GNU Free Documentation License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in +detail to address new problems or concerns. See +http://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. If the Document +specifies that a proxy can decide which future versions of this +License can be used, that proxy's public statement of acceptance of a +version permanently authorizes you to choose that version for the +Document. + +11. RELICENSING + +"Massive Multiauthor Collaboration Site" (or "MMC Site") means any +World Wide Web server that publishes copyrightable works and also +provides prominent facilities for anybody to edit those works. A +public wiki that anybody can edit is an example of such a server. A +"Massive Multiauthor Collaboration" (or "MMC") contained in the site +means any set of copyrightable works thus published on the MMC site. + +"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 +license published by Creative Commons Corporation, a not-for-profit +corporation with a principal place of business in San Francisco, +California, as well as future copyleft versions of that license +published by that same organization. + +"Incorporate" means to publish or republish a Document, in whole or in +part, as part of another Document. + +An MMC is "eligible for relicensing" if it is licensed under this +License, and if all works that were first published under this License +somewhere other than this MMC, and subsequently incorporated in whole or +in part into the MMC, (1) had no cover texts or invariant sections, and +(2) were thus incorporated prior to November 1, 2008. + +The operator of an MMC Site may republish an MMC contained in the site +under CC-BY-SA on the same site at any time before August 1, 2009, +provided the MMC is eligible for relicensing. + + +ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled "GNU + Free Documentation License". + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the "with...Texts." line with this: + + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..057268a --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +# This file is under the public domain. + +.PHONY: default pdf +.SUFFIXES: .tex .pdf + +default: pdf +pdf: coope.pdf + +# intentionally two-pass +.tex.pdf: + pdflatex $< $@ + pdflatex $< $@ + +clean: + rm -f coope.pdf diff --git a/README b/README new file mode 100644 index 0000000..52ab66b --- /dev/null +++ b/README @@ -0,0 +1,15 @@ +This article is free; see COPYING for additional information. + +This article, entitled "Classical Object-Oriented Programming with ECMAScript", +summarizes much of the research performed during the development of ease.js, as +classical object-oriented framework for ECMAScript/JavaScript. While the +information contained within the article is expressed in the author's own words, +an effort has been made to be as professional as possible to promote +modification without much distinction. + +It is the author's hope that this article will continue to be updated with +additional research as the project grows. Contributors to ease.js can feel free +to update the document with information that is pertinant. Likewise, individuals +who have a strong method of achieving an object-oriented style in ECMAScript can +feel free to contribute their own ideas, so long as it does not add considerable +bloat or muddy the point of the article. diff --git a/abstract.tex b/abstract.tex new file mode 100644 index 0000000..1d1d85d --- /dev/null +++ b/abstract.tex @@ -0,0 +1,22 @@ +\abstract +ECMAScript (more popularly known by the name ``JavaScript'') is the language of +the web. In the decades past, it has been used to augment web pages with trivial +features and obnoxious gimmicks. Today, the language is used to write +full-featured web applications that rival modern desktop software in nearly +every regard and has even expanded to create desktop and server software. With +increased usage, there is a desire to apply more familiar development paradigms +while continuing to take advantage of the language's incredibly flexible +functional and prototypal models. Of all of the modern paradigms, one of the +most influential and widely adopted is Classical Object-Oriented programming, as +represented in languages such as Java, C++, Python, Perl, PHP and others. +ECMAScript, as an object-oriented language, contains many features familiar to +Classical OO developers. However, certain features remain elusive. This article +will detail the development of a classical object-oriented framework for +ECMAScript, ease.js, which aims to address these issues by augmenting +ECMAScript's prototype model to allow the creation of familiar class-like +objects. This implementation enforces encapsulation and provides features that +most Classical OO developers take for granted until the time that ECMAScript +implements these features itself.\footnote{There was discussion of including +classes in ECMAScript 6 ``Harmony'', however it is not within the specification +at the time of writing. At that time, the framework could be used to transition +to ECMAScript's model, should the developer choose to do so.} diff --git a/coope.sty b/coope.sty new file mode 100644 index 0000000..67cd6d7 --- /dev/null +++ b/coope.sty @@ -0,0 +1,82 @@ +% COOPE style + +\usepackage{graphicx} +\usepackage[small,bf,justification=justified,singlelinecheck=false]{caption} +\usepackage[margin=.7in]{geometry} +\usepackage{listings} +\usepackage{color} +\usepackage{hyperref} + +\hypersetup{colorlinks, + citecolor=black, + filecolor=black, + linkcolor=black, + urlcolor=black, + bookmarksopen=true, + pdftex} + +\definecolor{gray}{rgb}{0.3,0.3,0.3} +\definecolor{darkgray}{rgb}{0.2,0.2,0.2} +\definecolor{lightgray}{rgb}{0.6,0.6,0.6} + +\newcommand{\jsref}{\textmd Listing~\ref} + +\lstdefinelanguage{JavaScript}{% + keywords={% + undefined,null,true,false,NaN,Infinity,return,% + try,catch,finally,function,var,if,then,else,% + in,while,do,done,case,break,continue% + }, + keywordstyle=\color{gray}, + comment=[l]{//}, + morecomment=[s]{/*}{*/}, + commentstyle=\color{lightgray} +} + +\lstset{% + language=JavaScript, + aboveskip=12pt, + columns=fullflexible, + keepspaces=true, + basicstyle=\small\ttfamily, + xleftmargin=18pt, + captionpos=b, + abovecaptionskip=12pt, + numbers=left, + numbersep=10pt, + numberstyle=\small\color{darkgray} +} + +\newenvironment{jsex}[2] + {% + \begin{lstlisting} + \captionof{#2} + \label{#1} + } + {% + \end{lstlisting} + } + +\newenvironment{jsexspan}[2] + {% + \begin{js*} + \caption{#2} + \label{lst:#1} + \begin{multicols}{2} + \columnwidth 10in + \small + } + {% + \end{multicols} + \end{js*} + } + +\newcommand{\dfn}{\emph} +\newcommand{\var}{\texttt} +\newcommand{\code}{\texttt} +\newcommand{\keyword}{\texttt} +\newcommand{\operator}{\texttt} +\newcommand{\func}{\texttt} + +\hyphenation{ECMA-Script Java-Script} + diff --git a/coope.tex b/coope.tex new file mode 100644 index 0000000..7642bff --- /dev/null +++ b/coope.tex @@ -0,0 +1,44 @@ +% Classical Object-Oriented Programming with ECMAScript +% +% Copyright (C) 2012 Mike Gerwitz +% +% Permission is granted to copy, distribute and/or modify this document under +% the terms of the GNU Free Documentation License, Version 1.3 or any later +% version published by the Free Software Foundation; with no Invariant +% Sections, no Front-Cover Texts and no Back-Cover Texts. A copy of the license +% is included in the section entitled "GNU Free Documentation License". +%% + +\documentclass[twocolumn]{article} +\input coope.sty + +\author{Mike Gerwitz} +\date{\today} + +\begin{document} + +\centerline{\Large \bf Classical Object-Oriented} +\centerline{\Large \bf Programming with ECMAScript} +\medskip +\centerline{\bf Mike Gerwitz} +\medskip +\centerline{\today} +\centerline{(Working Draft)} + +% Contributors: uncomment the following two lines and add your name(s). Please +% do not add yourself as an author unless you author a substantial portion of +% the text. +%\medskip +%\centerline{\bf{Contributors:} \textnormal{None}} +\medskip + +\input{abstract} + +\tableofcontents + +\input{sec/class-like} +\input{sec/hacking-proto} +\input{sec/encap-hacks} +\input{sec/licenses} + +\end{document} diff --git a/sec/class-like.tex b/sec/class-like.tex new file mode 100644 index 0000000..efeec7b --- /dev/null +++ b/sec/class-like.tex @@ -0,0 +1,543 @@ +\section{Class-Like Objects in ECMAScript} +\label{sec:class-like} +JavaScript is a multi-paradigm scripting language standardized by ECMAScript, +incorporating object-oriented, functional and imperative styles. The +Object-Oriented paradigm in itself supports two sub-paradigms - prototypal and +classical, the latter of which is popular in languages such as Java, C++, +Python, Perl, Ruby, Lisp, PHP, Smalltalk, among many others. ECMAScript itself +is prototypal. + +The creation of objects in ECMAScript can be as simple as using an object +literal, as defined by curly braces: + +\begin{verbatim} +var obj = { foo: "bar" }; +\end{verbatim} + +In a classical sense, object literals can be thought of as anonymous +singletons;\footnote{GOF.} that is, they have no name (they are identified by +the variable to which they are assigned) and only one instance of the literal +will exist throughout the life of the software.\footnote{Technically, one could +set the prototype of a constructor to be the object defined by the literal (see +\jsref{lst:proto-reuse}), however the resulting instances would be prototypes, +not instances of a common class shared by the literal and each subsequent +instance.} For example, calling a function that returns the same object literal +will return a distinct, entirely unrelated object for each invocation: + +\begin{verbatim} +function createObj() +{ + return { name: "foo" }; +} + +createObj() !== createObj(); +\end{verbatim} + +Using this method, we can create basic objects that act much like class +instances, as demonstrated in \jsref{lst:singleton}: + +\begin{lstlisting}[% + label=lst:singleton, + caption=A ``singleton'' with properties and methods +] +var obj = { + name: "Foo", + + setName: function( val ) + { + obj.name = val; + }, + + getName: function() + { + return obj.name; + } + }; + +obj.getName(); // "Foo" +obj.setName( "Bar" ); +obj.getName(); // "Bar" +\end{lstlisting} + +\subsection{Prototypes} +\label{sec:proto} +We could re-use \var{obj} in \jsref{lst:singleton} as a \dfn{prototype}, +allowing instances to inherit its members. For example: + +\begin{lstlisting}[% + label=lst:proto-reuse, + caption=Re-using objects as prototyes \bf{(bad)}, + firstnumber=last +] +function Foo() {} +Foo.prototype = obj; + +var inst1 = new Foo(), + inst2 = new Foo(); + +inst2.setName( "Bar" ); + +inst1.getName(); // "Bar" +inst2.getName(); // "Bar" +\end{lstlisting} + +In \jsref{lst:proto-reuse} above, we define \var{Foo} to be a +\dfn{constructor}\footnote{A ``constructor'' in ECMAScript is simply any +function intended to be invoked, often (but not always) with the \operator{new} +operator, that returns a new object whose members are derived from the +function's \var{prototype} property.} with our previous object \var{obj} as its +prototype. Unfortunately, as shown in \jsref{lst:singleton}, \var{name} is being +set on \var{obj} itself, which is a prototype shared between both instances. +Setting the name on one object therefore changes the name on the other (and, +indeed, all instances of \var{Foo}). To illustrate this important concept, +consider \jsref{lst:proto-mod} below, which continues from +\jsref{lst:proto-reuse}: + +\begin{lstlisting}[% + label=lst:proto-mod, + caption=The effect of prototypes on instances, + firstnumber=last +] +obj.foo = "bar"; +inst1.foo; // "bar" +inst2.foo; // "bar" +\end{lstlisting} + +Clearly, this is not how one would expect class-like objects to interact; each +object is expected to have its own state. When accessing a property of an +object, the members of the object itself are first checked. If the member +is not defined on the object itself,\footnote{Note that ``not defined'' does not +imply \emph{undefined}; \code{undefined} is a value.} then the prototype chain +is traversed. Therefore, we can give objects their own individual state by +defining the property on the individual instances, rather than the prototype, as +shown in \jsref{lst:inst-prop}.\footnote{Also demonstrated in +\jsref{lst:inst-prop} is the effect of the \keyword{delete} keyword, which +removes a member from an object, allowing the values of the prototype to ``peek +through`` as if a hole exists in the object. Setting the value to +\code{undefined} will not have the same effect, as it does not produce the +``hole''; the property would return \code{undefined} rather than the value on +the prototype.} + +\begin{lstlisting}[% + label=lst:inst-prop, + caption=Setting properties per-instance, + firstnumber=last +] +inst1.foo = "baz"; +inst1.foo; // "baz" +inst2.foo; // "bar" + +delete inst1.foo; +inst1.foo; // "bar" +\end{lstlisting} + +This does not entirely solve our problem. As shown in \jsref{lst:singleton}, our +\var{obj} prototype's methods (\func{getName()} and \func{setName()}) reference +\code{obj.name} - our prototype. \jsref{lst:proto-ref} demonstrates the problem +this causes when attempting to give each instance its own state in regards to +the \var{name} property: + +\begin{lstlisting}[% + label=lst:proto-ref, + caption=Referencing prototype values in \var{obj} causes problems with + per-instance data, + firstnumber=last +] +// ... + +inst1.name = "My Name"; +inst1.getName(); // "Foo" +\end{lstlisting} + +ECMAScript solves this issue with the \keyword{this} keyword. When a +method\footnote{A \dfn{method} is simply an invokable property of an object (a +function).} of an instance's prototype is invoked, \keyword{this} is bound, by +default,\footnote{One can override this default behavior with +\func{Function.call()} or \func{Function.apply()}.} to a reference of that +instance. Therefore, we can replace \var{obj} in \jsref{lst:singleton} with the +prototype definition in \jsref{lst:proto-proper} to solve the issue +demonstrated in \jsref{lst:proto-ref}: + +\begin{lstlisting}[% + label=lst:proto-proper, + caption=Re-using objects as prototypes \bf{(good)} +] +function Foo( name ) +{ + this.name = name; +}; + +Foo.prototype = { + setName = function( name ) + { + this.name = name; + }, + + getName = function() + { + return this.name; + } +}; + +var inst = new Foo( "Bar" ); +inst.name; // "Bar" +inst.getName(); // "Bar" + +inst.setName( "Baz" ); +inst.getName(); // "Baz" + +inst.name = "Foo"; +inst.getName(); // "Foo" +\end{lstlisting} + +\jsref{lst:proto-proper} shows that \keyword{this} is also bound to the new +instance from within the constructor; this allows us to initialize any +properties on the new instance before it is returned to the caller.\footnote{It +is worth mentioning that one can explicitly return an object from the +constructor, which will be returned in place of a new instance.} Evaluation of +the example yields an additional concern --- the observation that all object +members in ECMAScript are public.\footnote{That is not to say that encapsulation +is not possible; this statement is merely emphasizing that properties of objects +do not support access modifiers. We will get into the topic of encapsulation a +bit later.} Even though the \var{name} property was initialized within the +constructor, it is still accessible outside of both the constructor and the +prototype. Addressing this concern will prove to be an arduous process that +will be covered at great length in the following sections. For the time being, +we will continue discussion of conventional techniques, bringing us to the +concept of \dfn{privileged members}. + +\subsection{Privileged Members} +\label{sec:privileged} +The concept of \dfn{encapsulation} is a cornerstone of classical object-oriented +programming. Unfortunately, as \jsref{lst:proto-proper} demonstrates, it becomes +difficult to encapsulate data if all members of a given object are accessible +publicly. One means of addressing this issue is to take advantage of the fact +that functions introduce scope, allowing us to define a local variable (or use +an argument) within the constructor that is only accessible to the +\dfn{privileged member} \func{getName()}. + +\begin{lstlisting}[% + label=lst:privileged, + caption=Using privileged members to encapsulate data +] +function Foo( name ) +{ + this.getName = function() + { + return name; + }; + + this.setName = function( newname ) + { + name = newname; + }; +} +\end{lstlisting} + +If \var{name} in \jsref{lst:privileged} is encapsulated within the constructor, +our methods that \emph{access} that encapsulated data must \emph{too} be +declared within the constructor;\footnote{One may mix prototypes and privileged +members.} otherwise, if placed within the prototype, \var{name} would be out of +scope. This implementation has an unfortunate consequence --- our methods are +now being \emph{redeclared} each and every time a new instance of \var{Foo} is +created, which has obvious performance penalties.\footnote{As a general rule +of thumb, one should only use privileged members for methods that access +encapsulated data; all other members should be part of the prototype.} + +Due to these performance concerns, it is often undesirable to use privileged +members; many developers will instead simply prefix with an underscore members +intended to be private (e.g. \code{this.\_name}) while keeping all methods on +the prototype.\footnote{One example of a library that uses underscores in place +of privileged members is Dojo at http://dojotoolkit.org.} This serves as a clear +indicator that the API is not public, is subject to change in the future and +should not be touched. It also allows the property to be accessed by +subtypes,\footnote{The term ``subtype'' is not truly the correct term here. +Rather, the term in this context was meant to imply that an instance of the +constructor was used as the prototype for another constructor, acting much like +a subtype (child class).} acting like a protected member. Unfortunately, this +does not encapsulate the data, so the developer must trust that the user will +not tamper with it. + +\subsection{Subtypes and Polymorphism} +In classical terms, \dfn{subtyping} (also known as \dfn{subclassing}) is the act of +extending a \dfn{supertype} (creating a \dfn{child} class from a \dfn{parent}) +with additional functionality. The subtype is said to \dfn{inherit} its members +from the supertype.\footnote{In the case of languages that support access +modifiers, only public and protected members are inherited.} Based on our prior +examples in section~\ref{sec:proto}, one could clearly see how the prototype of +any constructor could be replaced with an instance of another constructor, +indefinitely, to achieve an inheritance-like effect. This useful consequence of +the prototype model is demonstrated in \jsref{lst:subtype}.\footnote{Unfortunately, a +responsible implementation is not all so elegant in practice.} + +\begin{lstlisting}[% + label=lst:subtype, + caption=Extending prototypes (creating subtypes) in ECMAScript +] +var SubFoo = function( name ) +{ + // call parent constructor + Foo.call( this, name ); +}; +SubFoo.prototype = new Foo(); +SubFoo.prototype.constructor = SubFoo; + +// build upon (extend) Foo +SubFoo.prototype.hello = function() +{ + return "Hello, " + this.name; +}; + +var inst = new SubFoo( "John" ); +inst.getName(); // "John" +inst.hello(); // "Hello, John" +\end{lstlisting} + +Consider the implications of \jsref{lst:subtype} with a close eye. This +extension of \var{Foo} is rather verbose. The first (and rather unpleasant fact +that may be terribly confusing to those fairly inexperienced with ECMAScript) +consideration to be made is \var{SubFoo}'s constructor. Note how the supertype +(\var{Foo}) must be invoked \emph{within the context of +\var{SubFoo}}\footnote{If \func{Function.call()} or \func{Function.apply()} are +not properly used, the function will, depending on the environment, assign +\keyword{this} to the global scope, which is absolutely not what one wants. In +strict mode, this effect is mitigated, but the result is still not what we +want.} in order to initialize the variables.\footnote{If the constructor accepts +more than a few arguments, one could simply do: \code{Foo.apply( this, arguments +);}} However, once properly deciphered, this call is very similar to invocation +of parent constructors in other languages. + +Following the definition of \var{SubFoo} is its prototype (line 6). Note from +section~\ref{sec:proto} that the prototype must contain the members that are to +be accessible to any instances of the constructor. If we were to simply assign +\var{Foo} to the prototype, this would have two terrible consequences, the +second of which will be discussed shortly. The first consequence would be that +all members of \var{Foo} \emph{itself} would be made available to instances of +\var{SubFoo}. In particular, you would find that \code{( new SubFoo() +).prototype === Foo.prototype}, which is hardly your intent. As such, we must +use a new instance of \var{Foo} for our prototype, so that the prototype +contains the appropriate members. + +We follow the prototype assignment with another alien declaration --- the +setting of \code{SubFoo.prototype.constructor} on line 7. To understand why this +is necessary, one must first understand that, given any object \var{o} such that +\code{var o = new O()}, \code{o.constructor === O}.\footnote{One could apply +this same concept to other core ECMAScript objects. For example, \code{( +function() \{\}).constructor === Function}, \code{[].constructor === Array}, +\code{\{\}.constructor === Object}, \code{true.constructor === Boolean} and +sofourth.} Recall from section~\ref{sec:proto} that values ``peek through +holes'' in the prototype chain. In this case, without our intervention, +\code{SubFoo.prototype.constructor === Foo} because \code{SubFoo.prototype = new +Foo()}. The \var{constructor} property is useful for reflection, so it is +important that we properly set this value to the appropriate constructor --- +\var{SubFoo}. Since \var{SubFoo.prototype} is an \emph{instance} of \var{Foo} +rather than \var{Foo} itself, the assignment will not directly affect \var{Foo}. +This brings us to our aforementioned second consequence of assigning +\code{SubFoo.prototype} to a \emph{new} instance of \var{Foo} --- extending the +prototype by adding to or altering existing values would otherwise change the +supertype's constructor, which would be an unintentional side-effect +that could have drastic consequences on the software. + +As an example of extending the prototype (we have already demonstrated +overwriting the \var{constructor} and this concept can be applied to overriding +any members of the supertype), method \var{hello()} has been included in +\jsref{lst:subtype} on line 10. Note that \keyword{this} will be bound to the +instance that the method is being invoked upon, since it is referenced within +the prototype. Also note that we are assigning the function in a slightly +different manner than in \jsref{lst:proto-proper}; this is necessary to ensure +that we do not overwrite the prototype we just declared. Any additional members +must be declared explicitly in this manner, which has the negative consequence +of further increasing the verbosity of the code. + +An instance of a subtype can be used in place of any of its supertypes in a +concept known as \dfn{polymorphism}. \jsref{lst:poly} demonstrates this concept +with \func{getFooName()}, a function that will return the name of any object of +type \var{Foo}.\footnote{Please note that the \operator{typeof} operator is not +appropriate in this situation, as both instances of \var{Foo} and \var{SubFoo} +would be considered typeof ``object''. The \operator{instanceof} operator is +appropriate when determining types of objects in terms of their +constructor.} + +\begin{lstlisting}[% + label=lst:poly, + caption=Polymorphism in ECMAScript +] +function getFooName( foo ) +{ + if ( !( foo instanceof Foo ) ) + { + throw TypeError( + "Expected instance of Foo" + ); + } + + return foo.getName(); +} + +var inst_parent = new Foo( "Parent" ), + inst_child = new SubFoo( "Child" ); + +getFooName( inst_parent ); // "Parent" +getFooName( inst_child ); // "Child" +getFooName( {} ); // throws TypeError +\end{lstlisting} + +The concepts demonstrated in this section could be easily used to extend +prototypes indefinitely, creating what is called a \dfn{prototype chain}. In the +case of an instance of \var{SubFoo}, the prototype chain of most environments +would likely be: \var{SubFoo}, \var{Foo}, \var{Object} (that is, +\code{Object.getPrototypeOf( new SubFoo() ) === SubFoo}, and so +fourth).\footnote{ECMAScript 5 introduces \code{Object.getPrototypeOf()}, which +allows retrieving the prototype of an object (instance). Some environments also +support the non-standard \var{\_\_proto\_\_} property, which is a JavaScript +extension.} Keep in mind, however, that the further down the prototype chain the +engine must traverse in order to find a given member, the greater the +performance impact. + +Due to the method used to ``extend'' prototypes, it should also be apparent that +multiple inheritance is unsupported by ECMAScript, as each each constructor may +only have one \var{prototype} property.\footnote{Multiple inheritance is +well-known for its problems. As an alternative, styles of programming similar to +the use of interfaces and traits/mixins in other languages are recommended and +are possible in ECMAScript.} + +\subsubsection{Extensible Constructors} +\label{sec:ext-ctor} +Before moving on from the topic of extending prototypes, the assignment of +\code{SubFoo.prototype} deserves some additional discussion. Consider the +implications of this assignment; particularity, the invocation of the +constructor \var{Foo}. ECMAScript does not perform assignments to prototypes +differently than any other assignment, meaning all the logic contained within +the constructor \var{Foo} will be executed. In our case, this does not have any +terrible consequences --- \var{name} will simply be initialized to +\code{undefined}, which will be overridden once \var{SubType} is invoked. +However, consider what may happen if \var{Foo} performed checks on its +arguments. + +\begin{lstlisting}[% + label=lst:ctor-problem, + caption=Potential constructor problems for prototype assignments +] +function Foo( name ) +{ + if ( typeof name !== 'string' ) + { + throw TypeError( "Invalid name" ); + } + + this.name = name; +} + +// ... +SubFoo.prototype = new Foo(); // TypeError +\end{lstlisting} + +As \jsref{lst:ctor-problem} shows, we can no longer use a new instance of +\var{Foo} as our prototype, unless we were to provide dummy data that will pass +any type checks and validations that the constructor performs. Dummy data is not +an ideal solution --- it muddies the code and will cause subtypes to break +should any validations be added to the supertype in the future.\footnote{Of +course, if the constructor of the supertype changes, there are always BC +(backwards-compatibility) concerns. However, in the case of validations in the +constructor, they may simply enforce already existing docblocks, which should +have already been adhered to.} Furthermore, all constructor logic will still be +performed. What if \var{Foo} were to do something considerably more intensive +--- perform vigorous data validations or initialize a database connection, +perhaps?\footnote{Constructors should take care in limiting what actions they +perform, especially if they produce side-effects.} Not only would we have to +provide potentially complicated dummy data or dummy/stubbed objects, our +prototype assignment would also incur an unnecessary performance hit. Indeed, +the construction logic would be performed \(n + 1\) times --- once for the +prototype and once for each instance, which would overwrite the results of the +previous constructor (or duplicate, depending on implementation). + +How one goes about solving this problem depends on the needs of the constructor. +Let us first consider a very basic solution --- ignoring constructor logic if +the provided argument list is empty, as is demonstrated in +\jsref{lst:ctor-ignore-empty}. + +\begin{lstlisting}[% + label=lst:ctor-ignore-empty, + caption=Ignoring construction logic if provided with an empty argument list +] +function Foo( name ) +{ + if ( arguments.length === 0 ) + { + return; + } + + // ... + + this.name = name; +} + +// ... +SubType.prototype = new Foo(); // OK +\end{lstlisting} + +This solution has its own problems. The most apparent issue is that one could +simply omit all constructor arguments to bypass constructor logic, which is +certainly undesirable.\footnote{Constructors allow us to initialize our object, +placing it in a consistent and predictable state. Allowing a user to bypass this +logic could not only introduce unintended consequences during the life of the +object, but would mandate additional checks during method calls to ensure the +current state is sane, which will add unnecessary overhead.} Secondly --- what +if \var{Foo}'s \var{name} parameter was optional and additional construction +logic needed to be performed regardless of whether or not \var{name} was +provided? Perhaps we would want to provide a default value for \var{name} in +addition to generating a random hash that can be used to uniquely identify each +instance of \var{Foo}. If we are immediately returning from the constructor when +all arguments are omitted, then such an implementation is not possible. Another +solution is needed in this case.\footnote{That is not to say that our first +solution --- immediately returning if no arguments are provided --- is useless. +This is a commonly used method that you may find useful for certain +circumstances.} + +A solution that satisfies all needs involves a more complicated hack that we +will defer to section~\ref{sec:extending}.\footnote{One may ask why, given all of +the complications of extending prototypes, one doesn't simply set +\code{SubFoo.prototype = Foo.prototype}. The reason for this is simple --- we +would not be able to extend the prototype without modifying the original, as +they would share references to the same object.} + +\subsection{Shortcomings} +ECMAScript's prototype model is highly flexible, but leaves much to be desired: + +\begin{description} + \item[Access Modifiers] + Classical OOP permits, generally, three common access modifiers: public, + protected and private. These access modifiers permit encapsulating data that + is unique \emph{per instance} of a given type, without the performance + penalties of privileged members (see \jsref{lst:privileged}). + + Not only are access modifiers unsupported, but the concept of protected + members is difficult difficult in ECMAScript. In order for a member to be + accessible to other objects higher up on the prototype chain (``subtypes''), + they must be public. Using privileged members would encapsulate the data + within the constructor, forcing the use of public methods to access the data + and disallowing method overrides, effectively destroying any chances of a + protected API.\footnote{As ease.js will demonstrate, protected APIs are + possible through a clever hack that would otherwise lead to terrible, + unmaintainable code.} + \item[Intuitive Subtyping] + Consider the verbosity of \jsref{lst:subtype}. Now imagine how much + duplicate code is required to maintain many subtypes in a large piece of + software. This only serves to distract developers from the actual business + logic of the prototype, forcing them to think in detailed terms of + prototypes rather than in terms of the problem domain.\footnote{The ability + to think within the problem domain rather than abstract machine concepts is + one of the key benefits of classical object-oriented programming.} + + Furthermore, as discussed in section~\ref{sec:ext-ctor}, creating extensible + constructors requires considerable thought that must be handled on a + case-by-case basis, or requires disproportionately complicated hacks (as + will be demonstrated in section~\ref{sec:extending}). +\end{description} + +Fortunately,\footnote{Well, fortunately in the sense that ECMAScript is flexible +enough that we can work around the issues. It is, however, terribly messy. In +ECMAScript's defense --- this is a consequence of the prototypal model; our +desire to use class-like objects instead of conventional prototypes produces the +necessity for these hacks.} those issues can be worked around with clever hacks, +allowing us to continue closer toward a classical development model. diff --git a/sec/encap-hacks.tex b/sec/encap-hacks.tex new file mode 100644 index 0000000..55c3b3c --- /dev/null +++ b/sec/encap-hacks.tex @@ -0,0 +1,131 @@ +\section{Encapsulating the Hacks} +Imagine jumping into a project in order to make a simple modification and then +seeing the code in \jsref{lst:prot-share}. This is a far cry from the simple +protected member declarations in traditional classical object-oriented +languages. In fact, there becomes a point where the hacks discussed in the +previous sections become unmaintainable messes that add a great deal of +boilerplate code with little use other than to distract from the actual +software itself. + +However, we do not have to settle for those messy implementations. Indeed, we +can come up with some fairly elegant and concise solutions by encapsulating the +hacks we have discussed into a classical object-oriented framework, library or +simple helper functions. Let's not get ahead of ourselves too quickly; we will +start exploring basic helper functions before we deal with diving into a full, +reusable framework. + +This section is intended for educational and experimental purposes. Before using +these examples to develop your own class system for ECMAScript, ensure that none +of the existing systems satisfy your needs; your effort is best suited toward +the advancement of existing projects than the segregation caused by the +introduction of additional, specialty frameworks.\footnote{That is not to +discourage experimentation. Indeed, one of the best, most exciting and fun ways +to learn about these concepts are to implement them yourself.} These are +discussed a bit later. + +\subsection{Constructor/Prototype Factory} +\label{sec:ctor-factory} +Section~\ref{sec:extending} offered one solution to the problem of creating a an +extensible constructor, allowing it to be used both to instantiate new objects +and as a prototype. Unfortunately, as \jsref{lst:ctor-extend} demonstrated, the +solution adds a bit of noise to the definition that will also be duplicated for +each constructor. The section ended with the promise of a cleaner, reusable +implementation. Perhaps we can provide that. + +Consider once again the issue at hand. The constructor, when called +conventionally with the \keyword{new} keyword to create a new instance, must +perform all of its construction logic. However, if we wish to use it as a +prototype, it is unlikely that we want to run \emph{any} of that logic --- we +are simply looking to have an object containing each of its members to use as a +prototype without the risk of modifying the prototype of the constructor in +question. Now consider how this issue is handled in other classical languages: +the \keyword{extend} keyword. + +ECMAScript has no such keyword, so we will have to work on an implementation +ourselves. We cannot use the name \func{extend()}, as it is a reserved +name;\footnote{Perhaps for future versions of ECMAScript.} as such, we will +start with a simple \func{Class} factory function with which we can create new +``classes'' without supertypes. We can than provide a \func{Class.extend()} +method to define a ``class'' with a supertype. + +\begin{lstlisting}[% + label=lst:ctor-factory, + caption=Constructor factory +] +var Class = ( function( extending ) +{ + var C = function( dfn ) + { + // extend from an empty base + return C.extend( null, dfn ); + }; + + C.extend = function( base, dfn ) + { + base = base || function() {}; + + var ctor = function() + { + // do nothing if extending + if ( extending ) + { + return; + } + + // call "actual" constructor + this.__construct && + this.__construct.apply( + this, arguments + ); + }; + + ctor.prototype = new base(); + ctor.prototype.constructor = ctor; + + copyTo( ctor.prototype, dfn ); + + return ctor; + }; + + function copyTo( dest, members ) + { + var hasOwn = Object.prototype + .hasOwnProperty; + + for ( var member in members ) + { + if ( !hasOwn.call( members, member ) ) + { + continue; + } + + dest[ member ] = members[ member ]; + } + }; + + return C; +} )( false ); + +var Foo = Class( +{ + __construct: function( name, ignore ) + { + ignore || throw Error( "Ctor called" ); + this._name = ''+( name ); + }, + + getName: function() + { + return this._name; + } +} ); + +var SubFoo = Class.extend( Foo, +{ + setName: function( name ) + { + this._name = ''+( name ); + } +} ); + +\end{lstlisting} diff --git a/sec/hacking-proto.tex b/sec/hacking-proto.tex new file mode 100644 index 0000000..683cad9 --- /dev/null +++ b/sec/hacking-proto.tex @@ -0,0 +1,956 @@ +\section{Hacking Around Prototypal Limitations} +Section~\ref{sec:class-like} demonstrated how one would work within the +limitations of conventional ECMAScript to produce class-like objects using +prototypes. For those coming from other classical object-oriented languages, +these features are insufficient. In order to address many of the remaining +issues, more elaborate solutions are necessary. + +It should be noted that all the hacks in this section will, in some way or +another, introduce additional overhead, although it should be minimal in +comparison with the remainder of the software that may implement them. +Performance considerations will be mentioned where the author finds it to be +appropriate. Do not let this concern deter you from using these solutions in +your own code --- always benchmark to determine where the bottleneck lies in +your software. + +\subsection{Extensible Constructors: Revisited} +\label{sec:extending} +Section~\ref{sec:ext-ctor} discussed improving constructor design to allow for +extensibility and to improve performance. However, the solution presented did +not provide a consistent means of creating extensible constructors with, for +example, optional argument lists. + +The only way to ensure that the constructor will bypass validation and +initialization logic only when used as a prototype is to somehow indicate that +it is being used as such. Since prototype assignment is in no way different than +any other assignment, no distinction can be made. As such, we must create our +own. + +\begin{lstlisting}[% + label=lst:ctor-extend, + caption=Working around prototype extending issues +] +var Foo = ( function( extending ) +{ + var F = function( name ) + { + if ( extending ) return; + + if ( typeof name !== 'string' ) + { + throw TypeError( "Invalid name" ); + } + + this.name = name || "Default"; + + // hypothetical; impl. left to reader + this.hash = createHash(); + }; + + F.asPrototype = function() + { + extending = true; + + var proto = new F(); + + extending = false; + return proto; + }; + + F.prototype = { + // getName(), etc... + }; + + return F; +} )( false ); + +function SubFoo() { /* ... */ } +SubFoo.prototype = Foo.asPrototype(); // OK +// ... + +var foo1 = new Foo(); +foo1.getName(); // "Default" +foo1.hash; // "..." + +var foo2 = new Foo( "Bar" ); +foo2.getName(); // "Bar" +foo2.hash; // "..." +\end{lstlisting} + +One solution, as demonstrated in \jsref{lst:ctor-extend}, is to use a variable +(e.g. \var{extending}) to indicate to a constructor when it is being used to +extend a prototype. The constructor, acting as a closure, can then check the +value of this flag to determine whether or not to immediately return, avoiding +all construction logic. This implementation would allow us to return only a +prototype, which is precisely what we are looking for. + +It is unlikely that we would want to expose \var{extending} directly for +modification, as this would involve manually setting the flag before requesting +the prototype, then remembering to reset it after we are done. Should the user +forget to reset the flag, all future calls to the constructor would continue to +ignore all constructor logic, which could lead to confusing bugs in the +software. To work around this issue, \jsref{lst:ctor-extend} offers an +\func{asPrototype()} method on \var{Foo} itself, which will set the flag, create +a new instance of \var{Foo}, reset the flag and return the new +instance.\footnote{In classical terms, \func{asPrototype()} can be thought of as +a static factory method of \var{Foo}.} + +In order to cleanly encapsulate our extension logic, \var{Foo} is generated +within a self-executing function (using much the same concept as privileged +members in section~\ref{sec:privileged}, with a slightly different +application).\footnote{Self-executing functions are most often used to introduce +scope, allowing for the encapsulation of certain data. In this case, we +encapsulate our extension logic and return our constructor (assigned to \var{F} +within the self-executing function), which is then assigned to \var{Foo}. Note +the parenthesis immediately following the anonymous function, which invokes it +with a single argument to give \var{extending} a default value of \code{false}. +This pattern of encapsulation and exporting specific values is commonly referred +to as the \dfn{Module Pattern}.} This gives \var{Foo} complete control over when +its constructor logic should be ignored. Of course, one would not want to +duplicate this mess of code for each and every constructor they create. +Factoring this logic into a common, re-usable implementation will be discussed a +bit later as part of a class system (see section~\ref{sec:ctor-factory}). + +\subsection{Encapsulating Data} +\label{sec:encap} +We discussed a basic means of encapsulation with privileged members in +section~\ref{sec:privileged}. Unfortunately, the solution, as demonstrated in +\jsref{lst:privileged}, involves redeclaring methods that could have otherwise +been defined within the prototype and shared between all instances. With that +goal in mind, let us consider how we may be able to share data for multiple +instances with a single method definition in the prototype. + +We already know from \jsref{lst:ctor-extend} that we can truly encapsulate data +for a prototype within a self-executing function. Methods can then, acting as +closures, access that data that is otherwise inaccessible to the remainder of +the software. With that example, we concerned ourselves with only a single piece +of data --- the \var{extending} flag. This data has no regard for individual +instances (one could think of it as static data, in classical terms). Using +\jsref{lst:ctor-extend} as a starting point, we can build a system that will +keep track of data \emph{per-instance}. This data will be accessible to all +prototype members. + +\subsubsection{A Naive Implementation} +\label{sec:encap-naive} +One approach to our problem involves to assigning each instance a unique +identifier (an ``instance id'', or \var{iid}). For our implementation, this +identifier will simply be defined as an integer that is incremented each time +the constructor is invoked.\footnote{There is, of course, a maximum +number of instances with this implementation. Once \var{iid} reaches +\var{Number.MAX\_NUMBER}, its next assignment will cause it to overflow to +\code{Number.POSITIVE\_INFINITY}. This number, however, can be rather large. On +one 64-bit system under v8, \code{Number.MAX\_NUMBER = +1.7976931348623157e+308.}} This instance id could be used as a key for a data +variable that stores data for each instance. Upon instantiation, the instance +id could be assigned to the new instance as a property (we'll worry about +methods of ``encapsulating'' this property later). + +\begin{lstlisting}[% + label=lst:encap-naive, + caption=Encapsulating data with shared members (a naive implementation) +] +var Stack = ( function() +{ + var idata = [], + iid = 0; + + var S = function() + { + // assign a unique instance identifier + // to each instance + this.__iid = iid++; + + idata[ this.__iid ] = { + stack: [] + }; + }; + + S.prototype = { + push: function( val ) + { + idata[ this.__iid ] + .stack.push( val ); + }, + + pop: function() + { + return idata[ this.__iid ] + .stack.pop(); + } + }; + + return S; +} )(); + +var first = new Stack(), + second = new Stack(); + +first.push( "foo" ); +second.push( "bar" ); + +first.pop(); // "foo" +second.pop(); // "bar" +\end{lstlisting} + +\jsref{lst:encap-naive} demonstrates a possible stack implementation using the +principals that have just been described. Just like \jsref{lst:ctor-extend}, a +self-executing function is used to encapsulate our data and returns the +\var{Stack} constructor.\footnote{The reader should take note that we have +omitted our extensible constructor solution discussed in +section~\ref{sec:extending} for the sake of brevity.} In addition to the +instance id, the instance data is stored in the array \var{idata} (an array is +appropriate here since \var{iid} is sequential and numeric). \var{idata} will +store an object for each instance, each acting in place of \keyword{this}. Upon +instantiation, the private properties for the new instance are initialized using +the newly assigned instance id. + +Because \var{idata} is not encapsulated within the constructor, we do not need +to use the concept of privileged members (see section~\ref{sec:privileged}); we +need only define the methods in such a way that \var{idata} is still within +scope. Fortunately, this allows us to define the methods on the prototype, +saving us method redeclarations with each call to the constructor, improving +overall performance. + +This implementation comes at the expense of brevity and creates a diversion from +common ECMAScript convention when accessing data for a particular instance using +prototypes. Rather than having ECMAScript handle this lookup process for us, we +must do so manually. The only data stored on the instance itself (bound to +\keyword{this}) is the instance id, \var{iid}, which is used to look up the +actual members from \var{idata}. Indeed, this is the first concern --- this is a +considerable amount of boilerplate code to create separately for each prototype +wishing to encapsulate data in this manner. + +An astute reader may raise concern over our \var{\_\_iid} assignment on each +instance. Firstly, although this name clearly states ``do not touch'' with its +double-underscore prefix,\footnote{Certain languages used double-underscore to +indicate something internal to the language or system. This also ensures the +name will not conflict with any private members that use the single-underscore +prefix convention.} the member is still public and enumerable.\footnote{The term +\dfn{enumerable} simply means that it can be returned by \keyword{foreach}.} +There is no reason why we should be advertising this internal data to the world. +Secondly, imagine what may happen if a user decides to alter the value of +\var{\_\_iid} for a given instance. Although such a modification would create +some fascinating (or even ``cool'') features, it could also wreak havoc on a +system and break encapsulation.\footnote{Consider that we know a stack is +encapsulated within another object. We could exploit this \var{\_\_iid} +vulnerability to gain access to the data of that encapsulated object as follows, +guessing or otherwise calculating the proper instance id: \code{( new Stack() +).\_\_iid = iid\_of\_encapsulated\_stack\_instance}.} + +In environments supporting ECMAScript 5 and later, we can make the property +non-enumerable and read-only using \code{Object.defineProperty()} in place of +the \var{\_\_iid} assignment: + +\begin{verbatim} +Object.defineProperty( this, '__iid', { + value: iid++, + + writable: false, + enumerable: false, + configurable: false +} ); +\end{verbatim} + +The \var{configurable} property simply determines whether or not we can +re-configure a property in the future using \code{Object.defineProperty()}. It +should also be noted that each of the properties, with the exception of +\var{value}, default to \code{false}, so they may be omitted; they were included +here for clarity. + +Of course, this solution leaves a couple loose ends: it will work only on +ECMAScript 5 and later environments (that have support for +\code{Object.defineProperty()}) and it still does not prevent someone from +spying on the instance id should they know the name of the property +(\var{\_\_iid}) ahead of time. However, we do need the instance id to be a +member of the instance itself for our lookup process to work properly. + +At this point, many developers would draw the line and call the solution +satisfactory. An internal id, although unencapsulated, provides little room for +exploitation.\footnote{You could get a total count of the number of instances of +a particular prototype, but not much else.} For the sake of discussion and the +development of a more concrete implementation, let us consider a potential +workaround for this issue. + +For pre-ES5\footnote{Hereinafter, ECMAScript 5 and ES5 will be used +interchangably.} environments, there will be no concrete solution, since all +properties will always be enumerable. However, we can make it more difficult by +randomizing the name of the \var{\_\_iid} property, which would require that the +user filter out all known properties or guess at the name. In ES5+ environments, +this would effectively eliminate the problem entirely,\footnote{Of course, +debuggers are always an option. There is also the possibility of exploiting +weaknesses in a random name implementation; one can never completely eliminate +the issue.} since the property name cannot be discovered or be known beforehand. +Consider, then, the addition of another variable within the self-executing +function --- \var{iid\_name} --- which we could set to some random value (the +implementation of which we will leave to the reader). Then, when initializing or +accessing values, one would use the syntax: + +\begin{verbatim} +idata[ this[ iid_name ] ].stack // ... +\end{verbatim} + +Of course, this introduces additional overhead, although it is likely to be +negligible in comparison with the rest of the software. + +With that, we have contrived a solution to our encapsulation problem. +Unfortunately, as the title of this section indicates, this implementation is +naive to a very important consideration --- memory consumption. The problem is +indeed so severe that this solution cannot possibly be recommended in practice, +although the core concepts have been an excellent experiment in ingenuity and +have provided a strong foundation on which to expand.\footnote{It is my hope +that the title of this section will help to encourage those readers that simply +skim for code to continue reading and consider the flaws of the design rather +than adopting them.} + +\subsubsection{A Proper Implementation} +\label{sec:encap-proper} +Section~\ref{sec:encap-naive} proposed an implementation that would permit the +true encapsulation of instance data, addressing the performance issues +demonstrated in \jsref{lst:privileged}. Unfortunately, the solution offered in +\jsref{lst:encap-naive} is prone to terrible memory leaks. In order to +understand why, we must first understand, on a very basic level, how garbage +collection (GC) is commonly implemented in environments that support ECMAScript. + +\dfn{Garbage collection} refers to an automatic cleaning of data (and +subsequent freeing of memory, details of which vary between implementations) +that is no longer ``used''. Rather than languages like C that require manual +allocation and freeing of memory, the various engines that implement ECMAScript +handle this process for us, allowing the developer to focus on the task at hand +rather than developing a memory management system for each piece of software. +Garbage collection can be a wonderful thing in most circumstances, but one must +understand how it recognizes data that is no longer being ``used'' in order to +ensure that the memory is properly freed. If data lingers in memory in such a +way that the software will not access it again and that the garbage collector is +not aware that the data can be freed, this is referred to as a \dfn{memory +leak}.\footnote{The term ``memory leak'' implies different details depending on +context --- in this case, it varies between languages. A memory leak in C is +handled much differently than a memory leak in ECMAScript environments. Indeed, +memory leaks in systems with garbage collectors could also be caused by bugs in +the GC process itself, although this is not the case here.} + +One method employed by garbage collectors is reference counting; when an object +is initially created, the reference count is set to one. When a reference to +that object is stored in another variable, that count is incremented by one. +When a variable containing a reference to a particular object falls out of +scope, is deleted, or has the value reassigned, the reference count is +decremented by one. Once the reference count reaches zero, it is scheduled for +garbage collection.\footnote{What happens after this point is +implementation-defined.} The concept is simple, but is complicated by the use of +closures. When an object is referenced within a closure, or even has the +\emph{potential} to be referenced through another object, it cannot be garbage +collected. + +In the case of \jsref{lst:encap-naive}, consider \var{idata}. With each new +instance, \var{iid} is incremented and an associated entry added to \var{idata}. +The problem is --- ECMAScript does not have destructor support. Since we cannot +tell when our object is GC'd, we cannot free the \var{idata} entry. Because each +and every object within \var{idata} has the \emph{potential} to be referenced at +some point in the future, even though our implementation does not allow for it, +it cannot be garbage collected. The reference count for each index of +\var{idata} will forever be $\geq 1$. + +To resolve this issue without altering this implementation, there is only one +solution --- to offer a method to call to manually mark the object as destroyed. +This defeats the purpose of garbage collection and is an unacceptable solution. +Therefore, our naive implementation contains a fatal design flaw. This extends +naturally into another question --- how do we work with garbage collection to +automatically free the data for us? + +The answer to this question is already known from nearly all of our prior +prototype examples. Unfortunately, it is an answer that we have been attempting +to work around in order to enforce encapsulation --- storing the data on the +instance itself. By doing so, the data is automatically freed (if the reference +count is zero, of course) when the instance itself is freed. Indeed, we have hit +a wall due to our inability to explicitly tell the garbage collector when the +data should be freed.\footnote{There may be an implementation out there +somewhere that does allow this, or a library that can interface with the garbage +collector. However, it would not be portable.} The solution is to find a common +ground between \jsref{lst:privileged} and \jsref{lst:encap-naive}. + +Recall our original goal --- to shy away from the negative performance impact of +privileged members without exposing each of our private members as public. Our +discussion has already revealed that we are forced to store our data on the +instance itself to ensure that it will be properly freed by the garbage +collector once the reference count reaches zero. Recall that +section~\ref{sec:encap-naive} provided us with a number of means of making our +only public member, \var{\_\_iid}, considerably more difficult to access, even +though it was not fully encapsulated. This same concept can be applied to our +instance data. + +\begin{lstlisting}[% + label=lst:encap-inst, + caption=Encapsulating data on the instance itself (see also + \jsref{lst:encap-naive}) +] +var Stack = ( function() +{ + // implementation left to reader + var _privname = genRandomName(); + + var S = function() + { + Object.defineProperty( this, _privname, { + enumerable: false, + writable: false, + configurable: false, + + value: { + stack: [] + } + } ); + }; + + S.prototype = { + push: function( val ) + { + this[ _privname ].stack.push( val ); + }, + + pop: function() + { + return this[ _privname ].stack.pop(); + } + }; + + return S; +} )(); +\end{lstlisting} + +\jsref{lst:encap-inst} uses a random, non-enumerable property to make the +discovery of the private data considerably more difficult.\footnote{The property +is also read-only, but that does not necessarily aid encapsulation. It prevents +the object itself from being reassigned, but not any of its members.} The random +name, \var{\_privname}, is used in each of the prototypes to look up the data on +the appropriate instance (e.g. \code{this[ \_privname ].stack} in place of +\code{this.stack}).\footnote{One may decide that the random name is unnecessary +overhead. However, note that static names would permit looking up the data if +the name is known ahead of time.} This has the same effect as +\jsref{lst:encap-naive}, with the exception that it is a bit easier to follow +without the instance management code and that it does not suffer from memory +leaks due to GC issues. + +Of course, this implementation depends on features introduced in ECMAScript 5 +--- namely, \code{Object.defineProperty()}, as introduced in +section~\ref{sec:encap-naive}. In order to support pre-ES5 environments, we +could define our own fallback \func{defineProperty()} method by directly +altering \var{Object},\footnote{The only circumstance I ever recommend modifying +built-in bojects/prototypes is to aid in backward compatibility; it is otherwise +a very poor practice that creates tightly coupled, unportable code.} as +demonstrated in \jsref{lst:defprop}. + +\begin{lstlisting}[% + label=lst:defprop, + caption=A fallback \code{Object.defineProperty()} implementation +] +Object.defineProperty = Object.defineProperty + || function( obj, name, config ) + { + obj[ name ] = config.value; + }; +\end{lstlisting} + +Unfortunately, a fallback implementation is not quite so simple. Certain +dialects may only partially implement \code{Object.createProperty()}. In +particular, I am referring to Internet Explorer 8's incomplete +implementation.\footnote{IE8's dialect is JScript.} Surprisingly, IE8 only +supports this action on DOM elements, not all objects. This puts us in a +terribly awkward situation --- the method is defined, but the implementation is +``broken''. As such, our simple and fairly concise solution in +\jsref{lst:defprop} is insufficient. Instead, we need to perform a more +complicated check to ensure that not only is the method defined, but also +functional for our particular uses. This check is demonstrated in +\jsref{lst:defprop-check}, resulting in a boolean value which can be used to +determine whether or not the fallback in \jsref{lst:defprop} is necessary. + +\begin{lstlisting}[% + label=lst:defprop-check, + caption=Working around IE8's incomplete \code{Object.defineProperty()} + implementation (taken from ease.js) +] +var can_define_prop = ( function() +{ + try + { + Object.defineProperty( {}, 'x', {} ); + } + catch ( e ) { return false; } + + return true; +} )(); +\end{lstlisting} + +This function performs two checks simultaneously --- it first checks to see if +\code{Object.defineProperty()} exists and then ensures that we are not using +IE8's broken implementation. If the invocation fails, that will mean that the +method does not exist (or is not properly defined), throwing an exception which +will immediately return false. If attempting to define a property using this +method on a non-DOM object in IE8, an exception will also be thrown, returning +false. Therefore, we can simply attempt to define a property on an empty object. +If this action succeeds, then \code{Object.defineProperty()} is assumed to be +sufficiently supported. The entire process is enclosed in a self-executing +function to ensure that the check is performed only once, rather than a function +that performs the check each time it is called. The merriment of this result to +\jsref{lst:defprop} is trivial and is left to the reader. + +It is clear from this fallback, however, that our property is enumerable in +pre-ES5 environments. At this point, a random property name would not be all +that helpful and the reader may decide to avoid the random implementation in its +entirety. + +\subsubsection{Private Methods} +\label{sec:priv-methods} +Thus far, we have been dealing almost exclusively with the issue of +encapsulating properties. Let us now shift our focus to the encapsulation of +other private members, namely methods (although this could just as easily be +applied to getters/setters in ES5+ environments). Private methods are actually +considerably easier to conceptualize, because the data does not vary between +instances --- a method is a method and is shared between all instances. As such, +we do not have to worry about the memory management issues addressed in +section~\ref{sec:encap-proper}. + +Encapsulating private members would simply imply moving the members outside of +the public prototype (that is, \code{Stack.prototype}). One would conventionally +implement private methods using privileged members (as in +section~\ref{sec:privileged}), but it is certainly pointless redefining the +methods for each instance, since \jsref{lst:encap-inst} provided us with a means +of accessing private data from within the public prototype. Since the +self-executing function introduces scope for our private data (instead of the +constructor), we do not need to redefine the methods for each new instance. +Instead, we can create what can be considered a second, private prototype. + +\begin{lstlisting}[% + label=lst:method-priv, + caption=Implementing shared private methods without privileged members +] +var Stack = ( function() +{ + var _privname = getRandomName(); + + var S = function() + { + // ... (see previous examples) + }; + + var priv_methods = { + getStack: function() + { + return this[ _privname ].stack; + } + }; + + S.prototype = { + push: function( val ) + { + var stack = priv_methods.getStack + .call( this ); + stack.push( val ); + }, + + pop: function() + { + var stack = priv_methods.getStack + .call( this ) + return stack.pop( val ); + } + }; + + return S; +} )(); +\end{lstlisting} + +\jsref{lst:method-priv} illustrates this concept of a private +prototype.\footnote{Alternatively, to reduce code at the expense of clarity, one +could simply define functions within the closure to act as private methods +without assigning them to \var{priv\_methods}. Note that \func{call()} is still +necessary in that situation.} The object \var{priv\_methods} acts as a second +prototype containing all members that are private and shared between all +instances, much like the conventional prototype. \code{Stack.prototype} then +includes only the members that are intended to be public. In this case, we have +defined a single private method --- \func{getStack()}. + +Recall how \keyword{this} is bound automatically for prototype methods (see +section~\ref{sec:proto}). ECMAScript is able to do this for us because of the +standardized \var{prototype} property. For our private methods, we have no such +luxury. Therefore, we are required to bind \keyword{this} to the proper object +ourselves through the use of \code{Function.call()} (or, alternatively, +\code{Function.apply()}). The first argument passed to \func{call()} is the +object to which \keyword{this} will be bound, which we will refer to as the +\dfn{context}. This, unfortunately, increases the verbosity of private method +calls, but successfully provides us with a private prototype implementation. + +Since private members needn't be inherited by subtypes, no additional work needs +to be done. + +\subsection{Protected Members} +We have thus far covered two of the three access modifiers (see +section~\ref{sec:encap}) --- public and private. Those implementations allowed +us to remain blissfully ignorant of inheritance, as public members are handled +automatically by ECMAScript and private members are never inherited by subtypes. +The concept of protected members is a bit more of an interesting study since it +requires that we put thought into providing subtypes with access to encapsulated +data, \emph{without} exposing this data to the rest of the world. + +From an implementation perspective, we can think of protected members much like +private; they cannot be part of the public prototype, so they must exist in +their own protected prototype and protected instance object. The only difference +here is that we need a way to expose this data to subtypes. This is an issue +complicated by our random name implementation (see +section~\ref{sec:encap-proper}); without it, subtypes would be able to access +protected members of its parent simply by accessing a standardized property +name. The problem with that is --- if subtypes can do it, so can other, +completely unrelated objects. As such, we will focus on a solution that works in +conjunction with our randomized name (an implementation with a standardized +name is trivial). + +In order for the data to remain encapsulated, the name must too remain +encapsulated. This means that the subtype cannot request the name from the +parent; instead, we must either have access to the random name or we must +\emph{tell} the parent what the name should be. The latter will not work +per-instance with the implementation described in +section~\ref{sec:encap-proper}, as the methods are not redefined per-instance +and therefore must share a common name. Let us therefore first consider the +simpler of options --- sharing a common protected name between the two classes. + +\begin{lstlisting}[% + label=lst:prot-share, + caption=Sharing protected members with subtypes +] +var _protname = getRandomName(); + +var Stack = ( function() +{ + var _privname = getRandomName(); + + var S = function() + { + // ... (see previous examples) + + Object.defineProperty( this, _privname, { + value: { stack: [] } + } ); + + Object.defineProperty( this, _protname, { + value: { empty: false } + } ); + }; + + // a means of sharing protected methods + Object.defineProperty( S, _protname, { + getStack: function() + { + return this[ _privname ].stack; + } + } ); + + S.prototype = { + push: function( val ) + { + var stack = S[ _protname ].getStack + .call( this ); + stack.push( val ); + + this[ _protname ].empty = false; + }, + + pop: function() + { + var stack = this[ _protname ] + .getStack.call( this ) + + this[ _protname ].empty = + ( stack.length === 0 ); + + return stack.pop( val ); + } + }; + + S.asPrototype = function() + { + // ... (see previous examples) + }; + + return S; +} )(); + + +var MaxStack = ( function() +{ + var M = function( max ) + { + // call parent constructor + Stack.call( this ); + + // we could add to our protected members + // (in practice, this would be private, not + // protected) + this[ _protname ].max = +max; + }; + + // override push + M.prototype.push = function( val ) + { + var stack = Stack[ _protname ].getStack + .call( this ); + + if ( stack.length === + this[ _protname ].max + ) + { + throw Error( "Maximum reached." ); + }; + + // call parent method + Stack.prototype.push.call( this, val ); + }; + + // add a new method demonstrating parent + // protected property access + M.prototype.isEmpty = function() + { + return this[ _protname ].empty; + }; + + M.prototype = Stack.asPrototype(); + M.prototype.constructor = M; + + return M; +} )(); + + +var max = new MaxStack( 2 ); +max.push( "foo" ); +max.push( "bar" ); +max.push( "baz" ); // Error +max.pop(); // "bar" +max.pop(); // "foo" +\end{lstlisting} + +\jsref{lst:prot-share} makes an attempt to demonstrate a protected property and +method implementation while still maintaining the distinction between it and the +private member implementation (see section~\ref{sec:encap-proper}). The example +contains two separate constructors --- \var{Stack} and \var{MaxStack}, the +latter of which extends \var{Stack} to limit the number of items that may be +pushed to it. \var{Stack} has been modified to include a protected property +\var{empty}, which will be set to \code{true} when the stack contains no items, +and a protected method \var{getStack()}, which both \var{Stack} and its subtype +\var{MaxStack} may use to access the private property \var{stack} of +\var{Stack}. + +The key part of this implementation is the declaration of \var{\_protname} +within the scope of both types (\var{Stack} and \var{MaxStack}).\footnote{One +would be wise to enclose all of \jsref{lst:prot-share} within a function to +prevent \var{\_protname} from being used elsewhere, exporting \var{Stack} and +\var{MaxStack} however the reader decides.} This declaration allows both +prototypes to access the protected properties just as we would the private +data. Note that \var{\_privname} is still defined individually within each type, +as this data is unique to each. + +Protected methods, however, need additional consideration. Private methods, when +defined within the self-executing function that returns the constructor, work +fine when called from within the associated prototype (see +section~\ref{sec:priv-methods}). However, since they're completely encapsulated, +we cannot use the same concept for protected methods --- the subtype would not +have access to the methods. Our two options are to either declare the protected +members outside of the self-executing function (as we do \var{\_privname}), which +makes little organizational sense, or to define the protected members on the +constructor itself using \var{\_protname} and +\code{Object.defineProperty()}\footnote{See section~\ref{sec:encap-proper} for +\code{Object.defineProperty()} workarounds/considerations.} to encapsulate it +the best we can. We can then use the shared \var{\_protname} to access the +methods on \var{Stack}, unknown to the rest of the world. + +An astute reader may realize that \jsref{lst:prot-share} does not permit the +addition of protected methods without also modifying the protected methods of +the supertype and all other subtypes; this is the same reason we assign new +instances of constructors to the \var{prototype} property. Additionally, +accessing a protected method further requires referencing the same constructor +on which it was defined. Fixing this implementation is left as an exercise to +the reader. + +Of course, there is another glaring problem with this implementation --- what +happens if we wish to extend one of our prototypes, but are not within the scope +of \var{\_protname} (which would be the case if you are using +\jsref{lst:prot-share} as a library, for example)? With this implementation, +that is not possible. As such, \jsref{lst:prot-share} is not recommended unless +you intended to have your prototypes act like final classes.\footnote{A +\dfn{final} class cannot be extended.} As this will not always be the case, we +must put additional thought into the development of a solution that allows +extending class-like objects with protected members outside of the scope of the +protected name \var{\_protname}. + +As we already discussed, we cannot request the protected member name from the +parent, as that will provide a means to exploit the implementation and gain +access to the protected members, thereby breaking encapsulation. Another +aforementioned option was \emph{telling} the parent what protected member name +to use, perhaps through the use of \func{asPrototype()} (see +section~\ref{sec:extending}). This is an option for protected \emph{properties}, +as they are initialized with each new instance, however it is not a clean +implementation for \emph{members}, as they have already been defined on the +constructor with the existing \var{\_protname}. Passing an alternative name +would result in something akin to: + +\begin{verbatim} +Object.defineProperty( S, _newname, { + value: S[ _protname ] +} ); +\end{verbatim} + +This would quickly accumulate many separate protected member references on the +constructor --- one for each subtype. As such, this implementation is also left +as an exercise for an interested reader; we will not explore it +further.\footnote{The reader is encouraged to attempt this implementation to +gain a better understanding of the concept. However, the author cannot recommend +its use in a production environment.} + +The second option is to avoid exposing protected property names entirely. This +can be done by defining a function that can expose the protected method object. +This method would use a system-wide protected member name to determine what +objects to return, but would never expose the name --- only the object +references. However, this does little to help us with our protected properties, +as a reference to that object cannot be returned until instantiation. As such, +one could use a partial implementation of the previously suggested +implementation in which one provides the protected member name to the parent(s). +Since the protected members would be returned, the duplicate reference issue +will be averted. + +The simplest means of demonstrating this concept is to define a function that +accepts a callback to be invoked with the protected method object. A more +elegant implementation will be described in future sections, so a full +implementation is also left as an exercise to the reader. \jsref{lst:prot-func} +illustrates a skeleton implementation.\footnote{Should the reader decide to take +up this exercise, keep in mind that the implementation should also work with +multiple supertypes (that is, type3 extends type2 extends type1).} The +\func{def} function accepts the aforementioned callback with an optional first +argument --- \var{base} --- from which to retrieve the protected methods. + +\begin{lstlisting}[% + label=lst:prot-func, + caption=Exposing protected methods with a callback (brief illustration; full + implementation left as an exercise for the reader) +] +var def = ( function() +{ + var _protname = getRandomName(); + + return function( base, callback ) + { + var args = Array.prototype.slice.call( + arguments + ), + + callback = args.pop(), + base = args.pop() || {}; + + return callback( base[ _protname ] ); + }; +} )(); + +var Stack = def( function( protm ) +{ + // ... + + return S; +} ); + +var MaxStack = def( Stack, function( protm ) +{ + // for properties only + var _protname = getRandomName(); + + // ... + + // asPrototype() would accept the protected + // member name + M.protoype = S.asPrototype( _protname ); + M.prototype.constructor = M; + + return M; +} ); +\end{lstlisting} + +\subsubsection{Protected Member Encapsulation Challenges} +Unfortunately, the aforementioned implementations do not change a simple fact +--- protected members are open to exploitation, unless the prototype containing +them cannot be extended outside of the library/implementation. Specifically, +there is nothing to prevent a user from extending the prototype and defining a +property or method to return the encapsulated members. + +Consider the implementation described in \jsref{lst:prot-func}. We could define +another subtype, \var{ExploitedStack}, as shown in \jsref{lst:prot-exploit}. +This malicious type exploits our implementation by defining two methods --- +\func{getProtectedProps()} and \func{getProtectedMethods()} --- that return +the otherwise encapsulated data. + +\begin{lstlisting}% +[label=lst:prot-exploit, + caption=Exploiting \jsref{lst:prot-func} by returning protected members. +] +var ExploitedStack = def( Stack, function( protm ) +{ + var _protname = getRandomName(); + + var E = function() { /* ... */ }; + + E.prototype.getProtectedProps = function() + { + return this[ _protname ]; + }: + + E.prototype.getProtectedMethods = function() + { + return protm; + }; + + E.prototype = Stack.asPrototype( _protname ); + E.prototype.constructor = E; + + return E; +} )(); +\end{lstlisting} + +Fortunately, our random \var{\_protname} implementation will only permit +returning data for the protected members of that particular instance. Had we not +used random names, there is a chance that an object could be passed to +\func{getProtectedProps()} and have its protected properties +returned.\footnote{Details depend on implementation. If a global protected +property name is used, this is trivial. Otherwise, it could be circumstantial +--- a matching name would have to be guessed, known, or happen by chance.} As +such, this property exploit is minimal and would only hurt that particular +instance. There could be an issue if supertypes contain sensitive protected +data, but this is an implementation issue (sensitive data should instead be +private). + +Methods, however, are a more considerable issue. Since the object exposed via +\func{def()} is \emph{shared} between each of the instances, much like its +parent prototype is, it can be used to exploit each and every instance (even if +the reader has amended \jsref{lst:prot-share} to resolve the aforementioned +protected member addition bug, since \code{Object.getPrototypeOf()} can be +used to work around this amendment). Someone could, for example, reassign +\code{Stack[ \_protname ].getStack()} to do something else; +\var{Object.defineProperty()} in \jsref{lst:prot-share} only made \code{Stack[ +\_protname ]} \emph{itself} read-only. The object itself, however, can be +modified. This can be amended by using \code{Object.defineProperty()} for each +and every protected method, which is highly verbose and cumbersome. + +Once we rule out the ability to modify protected method definitions,\footnote{Of +course, this doesn't secure the members in pre-ES5 environments.} we still must +deal with the issue of having our protected methods exposed and callable. For +example, one could do the following to gain access to the private \var{stack} +object: + +\begin{verbatim} +( new ExploitedStack() ).getProtectedMethods() + .getStack.call( some_stack_instance ); +\end{verbatim} + +Unfortunately, there is little we can do about this type of exploit besides +either binding\footnote{See \func{Function.bind()}.} each method call (which +would introduce additional overhead per instance) or entirely preventing the +extension of our prototypes outside of our own library/software. By creating a +protected API, you are exposing certain aspects of your prototype to the rest of +the world; this likely breaks encapsulation and, in itself, is often considered +a poor practice.\footnote{\code{Stack.getStack()} breaks encapsulation because +it exposes the private member \var{stack} to subtypes.} An alternative is to +avoid inheritance altogether and instead favor composition, thereby evading this +issue entirely. That is a pretty attractive concept, considering how verbose and +messy this protected hack has been. diff --git a/sec/licenses.tex b/sec/licenses.tex new file mode 100644 index 0000000..84b224d --- /dev/null +++ b/sec/licenses.tex @@ -0,0 +1,48 @@ +\section{Licenses} +This document and all source code contained within is free,\footnote{Free as in +``free speech'', not ``free beer''.} released under the GNU FDL. The source code +contained within the listings are also licensed under the GNU GPL, unless +otherwise noted within this section, to permit their use in free software. The +code listings are intended primarily for instruction and example and may not be +directly applicable to your software. If licensing is a concern, one may use +the listings to implement the code from scratch rather than using the code +verbatim. + +Each license may be found at http://www.gnu.org/licenses/. + +\subsection{Document License} +\begin{quote} +\small\texttt +Copyright \copyright{} 2012 Mike Gerwitz. + +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.3 +or any later version published by the Free Software Foundation; +with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. +A copy of the license is included in the section entitled "GNU +Free Documentation License". +\end{quote} + +\subsection{Code Listing License} +\begin{quote} +\small\texttt +Copyright \copyright{} 2012 Mike Gerwitz. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. +\end{quote} + +\subsubsection{Code Listing License Exceptions} +The following listings are provided under alternative licenses. + +\begin{description} + \small + \item[\jsref{lst:defprop-check}] GNU LGPL (taken from ease.js) +\end{description}