Encapsulating implemented list (can use isInstanceOf() or a future reflection API)
parent
0b112dac51
commit
c3b00ab9b7
90
lib/class.js
90
lib/class.js
|
@ -24,6 +24,16 @@
|
||||||
|
|
||||||
var util = require( './util' );
|
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
|
* 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 );
|
var class_new = exports.extend( dest );
|
||||||
|
getMeta( class_new ).implemented = implemented;
|
||||||
// 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() );
|
|
||||||
}
|
|
||||||
|
|
||||||
return class_new;
|
return class_new;
|
||||||
};
|
};
|
||||||
|
@ -130,6 +134,8 @@ exports.isClassInstance = function( obj )
|
||||||
*/
|
*/
|
||||||
exports.isInstanceOf = function( type, instance )
|
exports.isInstanceOf = function( type, instance )
|
||||||
{
|
{
|
||||||
|
var meta, implemented, i;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// check prototype chain (with throw an error if type is not a
|
// check prototype chain (with throw an error if type is not a
|
||||||
|
@ -141,16 +147,22 @@ exports.isInstanceOf = function( type, instance )
|
||||||
}
|
}
|
||||||
catch ( e ) {}
|
catch ( e ) {}
|
||||||
|
|
||||||
var implemented = type.implemented;
|
// if no metadata is available, then our remaining checks cannot be
|
||||||
if ( implemented instanceof Array )
|
// performed
|
||||||
|
if ( !( meta = getMeta( type ) ) )
|
||||||
{
|
{
|
||||||
var i = implemented.length;
|
return false;
|
||||||
while ( i-- )
|
}
|
||||||
|
|
||||||
|
implemented = meta.implemented;
|
||||||
|
i = implemented.length;
|
||||||
|
|
||||||
|
// check implemented interfaces
|
||||||
|
while ( i-- )
|
||||||
|
{
|
||||||
|
if ( implemented[ i ] === type )
|
||||||
{
|
{
|
||||||
if ( implemented[ i ] === type )
|
return true;
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,6 +284,9 @@ var extend = ( function( extending )
|
||||||
// members at runtime
|
// members at runtime
|
||||||
util.freeze( new_class );
|
util.freeze( new_class );
|
||||||
|
|
||||||
|
// create internal metadata for the new class
|
||||||
|
createMeta( new_class );
|
||||||
|
|
||||||
// we're done with the extension process
|
// we're done with the extension process
|
||||||
extending = false;
|
extending = false;
|
||||||
|
|
||||||
|
@ -352,7 +367,6 @@ function setupProps( func, abstract_methods )
|
||||||
{
|
{
|
||||||
attachAbstract( func, abstract_methods );
|
attachAbstract( func, abstract_methods );
|
||||||
attachExtend( func );
|
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
|
* Attaches partially applied isInstanceOf() method to class instance
|
||||||
*
|
*
|
||||||
|
@ -483,3 +484,34 @@ function attachInstanceOf( instance )
|
||||||
util.defineSecureProp( instance, 'isA', method );
|
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 ];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,24 +45,11 @@ assert.ok(
|
||||||
var Foo = {},
|
var Foo = {},
|
||||||
PlainFoo = Class.extend();
|
PlainFoo = Class.extend();
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
( PlainFoo.implemented instanceof Array ),
|
|
||||||
"Class contains empty list of implemented interfaces if " +
|
|
||||||
"none are implemented"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.doesNotThrow( function()
|
assert.doesNotThrow( function()
|
||||||
{
|
{
|
||||||
Foo = Class.implement( Type, Type2 );
|
Foo = Class.implement( Type, Type2 );
|
||||||
}, Error, "Class can implement interfaces" );
|
}, Error, "Class can implement interfaces" );
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
( ( Foo.implemented[ 0 ] === Type )
|
|
||||||
&& ( Foo.implemented[ 1 ] === Type2 )
|
|
||||||
),
|
|
||||||
"Class contains list of implemented interfaces"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
( ( Foo.prototype.foo instanceof Function )
|
( ( Foo.prototype.foo instanceof Function )
|
||||||
&& ( Foo.prototype.foo2 instanceof Function )
|
&& ( Foo.prototype.foo2 instanceof Function )
|
||||||
|
|
Loading…
Reference in New Issue