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 project at \url{http://easejs.org/manual/Implementation-Details.html}. The
manual contains implementation details and rationale for much of what will be
elaborated upon in this article.
elaborated upon in this paper.
\bibliographystyle{plain}
\bibliography{coope}

View File

@ -296,12 +296,12 @@ recommends the implementation in \jsref{lst:new-global-fix}.
\subsection{Private Member Encapsulation}
Section~\ref{sec:encap} discussed the encapsulation of private member data
by means of private property and method objects, avoiding the performance impact
of privileged members (see section~\ref{sec:privileged}). In order to avoid
memory leaks, the private data was stored on the instance itself rather than a
truly encapsulated object. The amount of code required for this implementation
is relatively small, but it is still repeated unnecessarily between all
constructors.
by means of private property and method objects, thereby avoiding the
performance impact of privileged members (see section~\ref{sec:privileged}). In
order to avoid memory leaks, the private data was stored on the instance itself
rather than a truly encapsulated object. The amount of code required for this
implementation was relatively small, but it is still repeated unnecessarily
between all constructors.
The private member implementation had two distinct pieces --- private
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
\fref{fig:proto-priv-cmp}). Properties, however, \emph{must} have their values
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
scalars). For the time being, we will focus on the method implementation and
leave the manual declaration of private properties to the \func{\_\_construct()}
method.
them (see \jsref{lst:proto-reuse}; note that this is not an issue for scalars).
For the time being, we will focus on the method implementation and leave the
manual declaration of private properties to the \func{\_\_construct()} method.
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.
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
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
object, which would expose a reference to the object (\jsref{lst:prot-func}).
However, this would be very unnatural and unintuitive. To keep our ``class''
declarations simple, another method is needed.
object, which would expose a reference to the object (as in
\jsref{lst:prot-func}). However, this would be very unnatural and unintuitive;
to keep our ``class'' declarations simple, another method is needed.
Consider the private member concept in a classical sense --- the data should be
available only to the methods of the class, but should not be accessible outside
of them. That is, given any class \code{C} with private property \code{C.\_priv}
and public method \code{C.getPrivValue()}, and an instance \code{i} of class
\code{C}, \code{i.\_priv} should not be defined unless within the context of
\code{i.getPrivValue()}. Consider then the only means of exposing that data to
the members of the prototype in ECMAScript without use of closures: through the
instance itself (\keyword{this}). This naturally derives an implementation that
had not been previously considered due to the impracticality of its use without
an automated factory --- exposing private members before a method invocation and
revoking them after the method has returned.
Consider the private member concept in a classical sense --- the private data
should be available only to the methods of the class and should not be
accessible outside of them. That is, given any class \code{C} with private
property \code{C.\_priv} and public method \code{C.getPrivValue()}, and an
instance \code{i} of class \code{C}, \code{i.\_priv} should not be defined
unless within the context of \code{i.getPrivValue()}. Consider then the only
means of exposing that data to the members of the prototype in ECMAScript
without use of closures: through the instance itself (\keyword{this}). This
naturally derives an implementation that had not been previously considered due
to the impracticality of its use without factory --- exposing
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
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.
\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.
Note how all arguments are forwarded to \var{func}, allowing us to invoke our
wrapped function as if it were the original. Also note the context in which
\var{func} is being called (the first argument of \func{apply()}). By 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
a wrapper within a prototype, as we \emph{must} bind \keyword{this} to the
function which invokes \var{func}, returning its value with a prefix and a
suffix. Note how all arguments are forwarded to \var{func}, allowing us to
invoke our wrapped function as if it were the original. Also note the context in
which \var{func} is being called (the first argument of \func{apply()}). By
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 a
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
prototype is demonstrated in \jsref{lst:func-wrap-ex} below.
@ -388,7 +388,7 @@ decision is minimal.}
\subfloat[%
Wrapper performance \emph{(invocation only)}. Operations per second rounded to
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}
\label{fig:func-wrap-perf-invoke}
@ -396,26 +396,26 @@ decision is minimal.}
\quad
\subfloat[%
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}
]{
\input{data/func-wrap-blogic.tex}
\label{fig:func-wrap-perf-blogic}
}
\caption{Function wrapping performance considerations. When measuring invocation
performance, the wrapper appears to be a terrible solution to any problem.
However, when considering the business logic the remainder of the software is
likely to contain, the effects of the wrapper are negligible. As such, worrying
about the wrapper is likely to be a micro-optimization, unless dealing with
call stack limitations. The wrapper in these tests simply invokes the wrapped
method with \code{Function.apply()}, forwarding all arguments.}
performance, the wrapper appears to be prohibitive. However, when considering
the business logic that the remainder of the software is likely to contain, the
effects of the wrapper are negligible. As such, worrying about the wrapper is
likely to be a micro-optimization, unless dealing with call stack limitations.
The wrapper in these tests simply invokes the wrapped method with
\code{Function.apply()}, forwarding all arguments.}
\label{fig:func-wrap-perf}
\end{figure*}
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
time a method is invoked. \fref{fig:func-wrap-perf-invoke} shows why this detail
is likely to be a concern --- invoking our wrapped function is so slow in
time a method is invoked. \frefpg{fig:func-wrap-perf-invoke} shows why this
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
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
@ -424,9 +424,9 @@ consideration the \emph{percent change between function invocations that contain
some sort of business logic}. This is precisely what
\frefpg{fig:func-wrap-perf-blogic} takes into consideration, showing that our
invocation worry is would actually be a micro-optimization. For example, in
software that performs DOM manipulation, the performance impact of
wrapper invocation is likely to be negligible due to repaints being highly
intensive operations.
software that performs DOM manipulation, the performance impact of wrapper
invocation is likely to be negligible due to repaints being highly intensive
operations.
One legitimate concern of our wrapper implementation, however, is limited
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}
\end{figure}
That said, call stack sizes for ECMAScript environments are growing increasingly
larger. Call stack limits for common browsers (including historical versions for
That said, call stack sizes for ECMAScript environments are growing ever larger.
Call stack limits for common browsers (including historical versions for
comparison) are listed in \frefpg{fig:stack-limits}. Should this limit be
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