1
0
Fork 0

Encapsulating implemented list (can use isInstanceOf() or a future reflection API)

closure/master
Mike Gerwitz 2011-01-03 23:41:45 -05:00
parent 0b112dac51
commit c3b00ab9b7
2 changed files with 61 additions and 42 deletions

View File

@ -24,6 +24,16 @@
var util = require( './util' );
/**
* Stores class metadata internally (ensures data is encapsulated)
*
* The data in this object is hashed on the class object itself. Therefore, to
* look up an item, you would provide the class itself as the property.
*
* @type {Object.<Object, { implemented: Array.<string> }>}
*/
var class_meta = {};
/**
* Creates a class, inheriting either from the provided base class or the
@ -66,13 +76,7 @@ exports.implement = function()
}
var class_new = exports.extend( dest );
// we cannot reassign the value since it is frozen, so copy the values into
// the array
while ( implemented[ 0 ] )
{
class_new.implemented.push( implemented.shift() );
}
getMeta( class_new ).implemented = implemented;
return class_new;
};
@ -130,6 +134,8 @@ exports.isClassInstance = function( obj )
*/
exports.isInstanceOf = function( type, instance )
{
var meta, implemented, i;
try
{
// check prototype chain (with throw an error if type is not a
@ -141,10 +147,17 @@ exports.isInstanceOf = function( type, instance )
}
catch ( e ) {}
var implemented = type.implemented;
if ( implemented instanceof Array )
// if no metadata is available, then our remaining checks cannot be
// performed
if ( !( meta = getMeta( type ) ) )
{
var i = implemented.length;
return false;
}
implemented = meta.implemented;
i = implemented.length;
// check implemented interfaces
while ( i-- )
{
if ( implemented[ i ] === type )
@ -152,7 +165,6 @@ exports.isInstanceOf = function( type, instance )
return true;
}
}
}
return false;
};
@ -272,6 +284,9 @@ var extend = ( function( extending )
// members at runtime
util.freeze( new_class );
// create internal metadata for the new class
createMeta( new_class );
// we're done with the extension process
extending = false;
@ -352,7 +367,6 @@ function setupProps( func, abstract_methods )
{
attachAbstract( func, abstract_methods );
attachExtend( func );
attachImplemented( func );
}
@ -452,19 +466,6 @@ function attachExtend( func )
}
/**
* Attaches empty list that may be populated with the implemented interfaces
*
* @param {Function} func function (class) to attach method to
*
* @return {undefined}
*/
function attachImplemented( func )
{
util.defineSecureProp( func, 'implemented', [] );
}
/**
* Attaches partially applied isInstanceOf() method to class instance
*
@ -483,3 +484,34 @@ function attachInstanceOf( instance )
util.defineSecureProp( instance, 'isA', method );
}
/**
* Initializes class metadata for the given class
*
* @param {Class} obj class to initialize metadata for
*
* @return {undefined}
*/
function createMeta( obj )
{
class_meta[ obj ] = {
implemented: [],
};
}
/**
* Returns reference to metadata for the requested class
*
* Since a reference is returned (rather than a copy), the returned object can
* be modified to alter the metadata.
*
* @param {Class} obj class to retrieve metadata for
*
* @return {Object}
*/
function getMeta( obj )
{
return class_meta[ obj ];
}

View File

@ -45,24 +45,11 @@ assert.ok(
var Foo = {},
PlainFoo = Class.extend();
assert.ok(
( PlainFoo.implemented instanceof Array ),
"Class contains empty list of implemented interfaces if " +
"none are implemented"
);
assert.doesNotThrow( function()
{
Foo = Class.implement( Type, Type2 );
}, Error, "Class can implement interfaces" );
assert.ok(
( ( Foo.implemented[ 0 ] === Type )
&& ( Foo.implemented[ 1 ] === Type2 )
),
"Class contains list of implemented interfaces"
);
assert.ok(
( ( Foo.prototype.foo instanceof Function )
&& ( Foo.prototype.foo2 instanceof Function )