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 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}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue