Minor corrections to "Private Member Encapsulation" section
parent
688634a883
commit
8c4d885541
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue