Next: , 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();

Figure 2.3: Using this for method chaining

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();

Figure 2.4: Accidentally revealing internal state via callback

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();

Figure 2.5: Providing public visibility object using this.__inst


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: , Up: Defining Classes   [Contents]