Next: Anonymous vs. Named Classes, Up: Defining Classes [Contents]
2.1.4 Class Caveats
ease.js tries to make classes act as in traditional Classical OOP as much as possible, but there are certain limitations, especially when supporting ECMAScript 3. These situations can cause some subtle bugs, so it’s important to note and understand them.
2.1.4.1 Returning Self
Returning this
is a common practice for method
chaining.2
In the majority of cases, this works fine in ease.js
(see also Temporary Classes):
var Foo = Class( 'Foo', { 'public beginning': function() { return this; }, 'public middle': function() { return this; }, 'public end': function() { // ... } } ); Foo().beginning().middle().end();
Within the context of the method, this
is a reference to
the privacy visibility object for that instance
(see 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 public visibility
object—the object that the rest of the world interacts with.
But what if you produce this
in some other context?
A callback, for example:
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();
In Figure 2.4,
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 this
is a reference to the private visibility object,
the callback has access to all its internal state,
and therefore the ability to set _foo
.
To solve this problem,
use this.__inst
,
which is a reference to the public visibility object
(the same one that ease.js would normally translate to on your
behalf):
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();
Footnotes
(2)
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.
Next: Anonymous vs. Named Classes, Up: Defining Classes [Contents]