Minor corrections to "Private Member Encapsulation" section

master
Mike Gerwitz 2012-04-06 00:10:23 -04:00
parent 688634a883
commit 8c4d885541
2 changed files with 48 additions and 48 deletions

View File

@ -47,7 +47,7 @@ development to include relevant information from the development of ease.js. In
the meantime, the reader is welcome to browse through the technical manual for the meantime, the reader is welcome to browse through the technical manual for
the project at \url{http://easejs.org/manual/Implementation-Details.html}. The the project at \url{http://easejs.org/manual/Implementation-Details.html}. The
manual contains implementation details and rationale for much of what will be manual contains implementation details and rationale for much of what will be
elaborated upon in this article. elaborated upon in this paper.
\bibliographystyle{plain} \bibliographystyle{plain}
\bibliography{coope} \bibliography{coope}

View File

@ -296,12 +296,12 @@ recommends the implementation in \jsref{lst:new-global-fix}.
\subsection{Private Member Encapsulation} \subsection{Private Member Encapsulation}
Section~\ref{sec:encap} discussed the encapsulation of private member data Section~\ref{sec:encap} discussed the encapsulation of private member data
by means of private property and method objects, avoiding the performance impact by means of private property and method objects, thereby avoiding the
of privileged members (see section~\ref{sec:privileged}). In order to avoid performance impact of privileged members (see section~\ref{sec:privileged}). In
memory leaks, the private data was stored on the instance itself rather than a order to avoid memory leaks, the private data was stored on the instance itself
truly encapsulated object. The amount of code required for this implementation rather than a truly encapsulated object. The amount of code required for this
is relatively small, but it is still repeated unnecessarily between all implementation was relatively small, but it is still repeated unnecessarily
constructors. between all constructors.
The private member implementation had two distinct pieces --- private The private member implementation had two distinct pieces --- private
properties, as demonstrated in \jsref{lst:encap-inst}, and private methods, as properties, as demonstrated in \jsref{lst:encap-inst}, and private methods, as
@ -309,32 +309,32 @@ demonstrated in \jsref{lst:method-priv}. This distinction is important, as
private methods should not be redefined for each new instance (see private methods should not be redefined for each new instance (see
\fref{fig:proto-priv-cmp}). Properties, however, \emph{must} have their values \fref{fig:proto-priv-cmp}). Properties, however, \emph{must} have their values
copied for each new instance to prevent references from being shared between copied for each new instance to prevent references from being shared between
multiple instances (see \jsref{lst:proto-reuse}; this is not an issue for them (see \jsref{lst:proto-reuse}; note that this is not an issue for scalars).
scalars). For the time being, we will focus on the method implementation and For the time being, we will focus on the method implementation and leave the
leave the manual declaration of private properties to the \func{\_\_construct()} manual declaration of private properties to the \func{\_\_construct()} method.
method.
The listings in section~\ref{sec:encap} were derived from a simple concept --- The listings in section~\ref{sec:encap} were derived from a simple concept ---
the private member objects were within the scope of the prototype members. the private member objects were within the scope of the prototype members.
However, if we are encapsulating this hack within our constructor factory, then However, if we are to encapsulate this hack within our constructor factory, then
the members (the definition object) would be declared \emph{outside} the scope the members (the definition object) would be declared \emph{outside} the scope
of any private member objects that are hidden within our factory. To expose the of any private member objects that are hidden within our factory. To expose the
private ``prototype'' object, we could accept a function instead of a definition private ``prototype'' object, we could accept a function instead of a definition
object, which would expose a reference to the object (\jsref{lst:prot-func}). object, which would expose a reference to the object (as in
However, this would be very unnatural and unintuitive. To keep our ``class'' \jsref{lst:prot-func}). However, this would be very unnatural and unintuitive;
declarations simple, another method is needed. to keep our ``class'' declarations simple, another method is needed.
Consider the private member concept in a classical sense --- the data should be Consider the private member concept in a classical sense --- the private data
available only to the methods of the class, but should not be accessible outside should be available only to the methods of the class and should not be
of them. That is, given any class \code{C} with private property \code{C.\_priv} accessible outside of them. That is, given any class \code{C} with private
and public method \code{C.getPrivValue()}, and an instance \code{i} of class property \code{C.\_priv} and public method \code{C.getPrivValue()}, and an
\code{C}, \code{i.\_priv} should not be defined unless within the context of instance \code{i} of class \code{C}, \code{i.\_priv} should not be defined
\code{i.getPrivValue()}. Consider then the only means of exposing that data to unless within the context of \code{i.getPrivValue()}. Consider then the only
the members of the prototype in ECMAScript without use of closures: through the means of exposing that data to the members of the prototype in ECMAScript
instance itself (\keyword{this}). This naturally derives an implementation that without use of closures: through the instance itself (\keyword{this}). This
had not been previously considered due to the impracticality of its use without naturally derives an implementation that had not been previously considered due
an automated factory --- exposing private members before a method invocation and to the impracticality of its use without factory --- exposing
revoking them after the method has returned. private members before a method invocation and revoking them after the method
has returned.
To accomplish this, the factory must be able to intelligently determine when a To accomplish this, the factory must be able to intelligently determine when a
method is being invoked. This leads us into a somewhat sensitive topic --- method is being invoked. This leads us into a somewhat sensitive topic ---
@ -356,13 +356,13 @@ can also be easily extended to create \dfn{partially applied functions}.}
\jsref{lst:func-wrap} demonstrates the basic concept of a function wrapper. \jsref{lst:func-wrap} demonstrates the basic concept of a function wrapper.
\func{wrap()} accepts a single argument, \var{func}, and returns a new anonymous \func{wrap()} accepts a single argument, \var{func}, and returns a new anonymous
function which invokes \var{func}, returning its value with a prefix and suffix. function which invokes \var{func}, returning its value with a prefix and a
Note how all arguments are forwarded to \var{func}, allowing us to invoke our suffix. Note how all arguments are forwarded to \var{func}, allowing us to
wrapped function as if it were the original. Also note the context in which invoke our wrapped function as if it were the original. Also note the context in
\var{func} is being called (the first argument of \func{apply()}). By binding which \var{func} is being called (the first argument of \func{apply()}). By
\keyword{this} of \var{func} to \keyword{this} of our wrapper, we are binding \keyword{this} of \var{func} to \keyword{this} of our wrapper, we are
effectively forwarding it. This detail is especially important if we are using effectively forwarding it. This detail is especially important if we are using a
a wrapper within a prototype, as we \emph{must} bind \keyword{this} to the wrapper within a prototype, as we \emph{must} bind \keyword{this} to the
instance that the method is being invoked upon. Use of \func{wrap()} with a instance that the method is being invoked upon. Use of \func{wrap()} with a
prototype is demonstrated in \jsref{lst:func-wrap-ex} below. prototype is demonstrated in \jsref{lst:func-wrap-ex} below.
@ -388,7 +388,7 @@ decision is minimal.}
\subfloat[% \subfloat[%
Wrapper performance \emph{(invocation only)}. Operations per second rounded to Wrapper performance \emph{(invocation only)}. Operations per second rounded to
millions.\cite{jsperf-func-wrap} Numbers in parenthesis indicate percent millions.\cite{jsperf-func-wrap} Numbers in parenthesis indicate percent
change between the two values, indicating a significant performance loss. change between the two values, demonstrating a significant performance loss.
]{ ]{
\input{data/func-wrap-invoke.tex} \input{data/func-wrap-invoke.tex}
\label{fig:func-wrap-perf-invoke} \label{fig:func-wrap-perf-invoke}
@ -396,26 +396,26 @@ decision is minimal.}
\quad \quad
\subfloat[% \subfloat[%
Wrapper performance \emph{with business logic} Wrapper performance \emph{with business logic}
(\code{(new Array(100)).join(',|').split('|')}); performance (\code{(new Array(100)).join('|').split('|')}); performance
impact is negligible. Operations per second.\cite{jsperf-func-wrap-blogic} impact is negligible. Operations per second.\cite{jsperf-func-wrap-blogic}
]{ ]{
\input{data/func-wrap-blogic.tex} \input{data/func-wrap-blogic.tex}
\label{fig:func-wrap-perf-blogic} \label{fig:func-wrap-perf-blogic}
} }
\caption{Function wrapping performance considerations. When measuring invocation \caption{Function wrapping performance considerations. When measuring invocation
performance, the wrapper appears to be a terrible solution to any problem. performance, the wrapper appears to be prohibitive. However, when considering
However, when considering the business logic the remainder of the software is the business logic that the remainder of the software is likely to contain, the
likely to contain, the effects of the wrapper are negligible. As such, worrying effects of the wrapper are negligible. As such, worrying about the wrapper is
about the wrapper is likely to be a micro-optimization, unless dealing with likely to be a micro-optimization, unless dealing with call stack limitations.
call stack limitations. The wrapper in these tests simply invokes the wrapped The wrapper in these tests simply invokes the wrapped method with
method with \code{Function.apply()}, forwarding all arguments.} \code{Function.apply()}, forwarding all arguments.}
\label{fig:func-wrap-perf} \label{fig:func-wrap-perf}
\end{figure*} \end{figure*}
Many readers are likely to be concerned about a decision that wraps every Many readers are likely to be concerned about a decision that wraps every
function of our definition object, as this will require two function calls each function of our definition object, as this will require two function calls each
time a method is invoked. \fref{fig:func-wrap-perf-invoke} shows why this detail time a method is invoked. \frefpg{fig:func-wrap-perf-invoke} shows why this
is likely to be a concern --- invoking our wrapped function is so slow in detail is likely to be a concern --- invoking our wrapped function is so slow in
comparison to invoking the original function directly that the solution seems comparison to invoking the original function directly that the solution seems
prohibitive. However, one must consider how functions are \emph{actually} used prohibitive. However, one must consider how functions are \emph{actually} used
--- to perform some sort of business logic. It is rare that we would invoke --- to perform some sort of business logic. It is rare that we would invoke
@ -424,9 +424,9 @@ consideration the \emph{percent change between function invocations that contain
some sort of business logic}. This is precisely what some sort of business logic}. This is precisely what
\frefpg{fig:func-wrap-perf-blogic} takes into consideration, showing that our \frefpg{fig:func-wrap-perf-blogic} takes into consideration, showing that our
invocation worry is would actually be a micro-optimization. For example, in invocation worry is would actually be a micro-optimization. For example, in
software that performs DOM manipulation, the performance impact of software that performs DOM manipulation, the performance impact of wrapper
wrapper invocation is likely to be negligible due to repaints being highly invocation is likely to be negligible due to repaints being highly intensive
intensive operations. operations.
One legitimate concern of our wrapper implementation, however, is limited One legitimate concern of our wrapper implementation, however, is limited
call stack space. The wrapper will effectively cut the remaining stack space in call stack space. The wrapper will effectively cut the remaining stack space in
@ -449,8 +449,8 @@ functions and its result returned by a wrapped method call.
\label{fig:stack-limits} \label{fig:stack-limits}
\end{figure} \end{figure}
That said, call stack sizes for ECMAScript environments are growing increasingly That said, call stack sizes for ECMAScript environments are growing ever larger.
larger. Call stack limits for common browsers (including historical versions for Call stack limits for common browsers (including historical versions for
comparison) are listed in \frefpg{fig:stack-limits}. Should this limit be comparison) are listed in \frefpg{fig:stack-limits}. Should this limit be
reached, another alternative is to use \func{setTimeout()} to reset the stack reached, another alternative is to use \func{setTimeout()} to reset the stack
and continue the recursive operation. This can also have the benefit of making and continue the recursive operation. This can also have the benefit of making