Add "Class Caveats" manual section
This will grow. * doc/classes.texi (Class Caveats): Added with `this.__inst' documentationmaster
parent
30e7feefc9
commit
ef5eade499
137
doc/classes.texi
137
doc/classes.texi
|
@ -251,12 +251,149 @@ variable @var{MyClass}. By convention, we use CamelCase, with the first
|
||||||
letter capital, for class names (and nothing else).
|
letter capital, for class names (and nothing else).
|
||||||
|
|
||||||
@menu
|
@menu
|
||||||
|
* Class Caveats:: Important things to note about using ease.js classes
|
||||||
* Anonymous vs. Named Classes::
|
* Anonymous vs. Named Classes::
|
||||||
* Constructors:: How to declare a constructor
|
* Constructors:: How to declare a constructor
|
||||||
* Temporary Classes:: Throwaway classes that only need to be used once
|
* Temporary Classes:: Throwaway classes that only need to be used once
|
||||||
* Temporary Instances:: Throwaway instances that only need to be used once
|
* Temporary Instances:: Throwaway instances that only need to be used once
|
||||||
@end menu
|
@end menu
|
||||||
|
|
||||||
|
|
||||||
|
@node Class Caveats
|
||||||
|
@subsection Class Caveats
|
||||||
|
ease.js tries to make classes act as in traditional Classical@tie{}OOP
|
||||||
|
as much as possible,
|
||||||
|
but there are certain limitations,
|
||||||
|
especially when supporting ECMAScript@tie{}3.
|
||||||
|
These situations can cause some subtle bugs,
|
||||||
|
so it's important to note and understand them.
|
||||||
|
|
||||||
|
@subsubsection Returning Self
|
||||||
|
Returning @code{this} is a common practice for method
|
||||||
|
chaining.@footnote{
|
||||||
|
An interface that performs method chaining is less frequently
|
||||||
|
referred to as a ``fluent interface''.
|
||||||
|
This manual does not use that terminology.
|
||||||
|
Note also that method chaining implies that the class has state:
|
||||||
|
consider making your objects immutable instead,
|
||||||
|
which creates code that is easier to reason about.}
|
||||||
|
In the majority of cases, this works fine in ease.js
|
||||||
|
(see also @ref{Temporary Classes}):
|
||||||
|
|
||||||
|
@float Figure, f:method-chain
|
||||||
|
@verbatim
|
||||||
|
var Foo = Class( 'Foo',
|
||||||
|
{
|
||||||
|
'public beginning': function()
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
'public middle': function()
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
'public end': function()
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
Foo().beginning().middle().end();
|
||||||
|
@end verbatim
|
||||||
|
@caption{Using @code{this} for method chaining}
|
||||||
|
@end float
|
||||||
|
|
||||||
|
Within the context of the method, @code{this} is a reference to
|
||||||
|
the@tie{}privacy visibility object for that instance
|
||||||
|
(@pxref{The Visibility Object}).
|
||||||
|
That is---it exposes all of the object's internal state.
|
||||||
|
When it is returned from a method call, ease.js recognizes this and
|
||||||
|
replaces it with a reference to the @emph{public} visibility
|
||||||
|
object---the object that the rest of the world interacts with.
|
||||||
|
|
||||||
|
But what if you produce @code{this} in some other context?
|
||||||
|
A callback, for example:
|
||||||
|
|
||||||
|
@float Figure, f:method-this-callback
|
||||||
|
@verbatim
|
||||||
|
var Foo = Class( 'Foo',
|
||||||
|
{
|
||||||
|
'private _foo': 'good',
|
||||||
|
|
||||||
|
'public beginning': function( c )
|
||||||
|
{
|
||||||
|
// XXX: `this' is the private visibility object
|
||||||
|
c( this );
|
||||||
|
},
|
||||||
|
|
||||||
|
'public end': function()
|
||||||
|
{
|
||||||
|
return this._foo;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
// result: 'bad'
|
||||||
|
Foo()
|
||||||
|
.beginning( function( self )
|
||||||
|
{
|
||||||
|
// has access to internal state
|
||||||
|
self._foo = 'bad';
|
||||||
|
} )
|
||||||
|
.end();
|
||||||
|
@end verbatim
|
||||||
|
@caption{Accidentally revealing internal state via callback}
|
||||||
|
@end float
|
||||||
|
|
||||||
|
In @ref{f:method-this-callback},
|
||||||
|
@code{beginning} applies the callback with a reference to what most
|
||||||
|
would believe to be the class instance
|
||||||
|
(which is a reasonable assumption,
|
||||||
|
considering that ease.js usually maintains that facade).
|
||||||
|
Since @code{this} is a reference to the private visibility object,
|
||||||
|
the callback has access to all its internal state,
|
||||||
|
and therefore the ability to set @code{_foo}.
|
||||||
|
|
||||||
|
To solve this problem,
|
||||||
|
use @code{this.__inst},
|
||||||
|
which is a reference to the @emph{public} visibility object
|
||||||
|
(the same one that ease.js would normally translate to on your
|
||||||
|
behalf):
|
||||||
|
|
||||||
|
@float Figure, f:method-callback-inst
|
||||||
|
@verbatim
|
||||||
|
var Foo = Class( 'Foo',
|
||||||
|
{
|
||||||
|
'private _foo': 'good',
|
||||||
|
|
||||||
|
'public beginning': function( c )
|
||||||
|
{
|
||||||
|
// OK
|
||||||
|
c( this.__inst );
|
||||||
|
},
|
||||||
|
|
||||||
|
'public end': function()
|
||||||
|
{
|
||||||
|
return this._foo;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
// result: 'good'
|
||||||
|
Foo()
|
||||||
|
.beginning( function( self )
|
||||||
|
{
|
||||||
|
// sets public property `_foo', since `self' is now the public
|
||||||
|
// visibility object
|
||||||
|
self._foo = 'bad';
|
||||||
|
} )
|
||||||
|
.end();
|
||||||
|
@end verbatim
|
||||||
|
@caption{Providing public visibility object using @code{this.__inst}}
|
||||||
|
@end float
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@node Anonymous vs. Named Classes
|
@node Anonymous vs. Named Classes
|
||||||
@subsection Anonymous vs. Named Classes
|
@subsection Anonymous vs. Named Classes
|
||||||
We state that @ref{f:class-easejs,} declared an @dfn{anyonmous class}
|
We state that @ref{f:class-easejs,} declared an @dfn{anyonmous class}
|
||||||
|
|
Loading…
Reference in New Issue