isInstanceOf() now works with interfaces
parent
c3b00ab9b7
commit
56c13b757b
72
lib/class.js
72
lib/class.js
|
@ -27,10 +27,9 @@ var util = require( './util' );
|
||||||
/**
|
/**
|
||||||
* Stores class metadata internally (ensures data is encapsulated)
|
* Stores class metadata internally (ensures data is encapsulated)
|
||||||
*
|
*
|
||||||
* The data in this object is hashed on the class object itself. Therefore, to
|
* The data in this object is hashed a class id.
|
||||||
* look up an item, you would provide the class itself as the property.
|
|
||||||
*
|
*
|
||||||
* @type {Object.<Object, { implemented: Array.<string> }>}
|
* @type {Object.<number, { implemented: Array.<string> }>}
|
||||||
*/
|
*/
|
||||||
var class_meta = {};
|
var class_meta = {};
|
||||||
|
|
||||||
|
@ -76,7 +75,7 @@ exports.implement = function()
|
||||||
}
|
}
|
||||||
|
|
||||||
var class_new = exports.extend( dest );
|
var class_new = exports.extend( dest );
|
||||||
getMeta( class_new ).implemented = implemented;
|
getMeta( class_new.__cid ).implemented = implemented;
|
||||||
|
|
||||||
return class_new;
|
return class_new;
|
||||||
};
|
};
|
||||||
|
@ -149,8 +148,9 @@ exports.isInstanceOf = function( type, instance )
|
||||||
|
|
||||||
// if no metadata is available, then our remaining checks cannot be
|
// if no metadata is available, then our remaining checks cannot be
|
||||||
// performed
|
// performed
|
||||||
if ( !( meta = getMeta( type ) ) )
|
if ( !instance.__cid || !( meta = getMeta( instance.__cid ) ) )
|
||||||
{
|
{
|
||||||
|
console.log( instance );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,6 +203,8 @@ function Class() {};
|
||||||
*/
|
*/
|
||||||
var extend = ( function( extending )
|
var extend = ( function( extending )
|
||||||
{
|
{
|
||||||
|
var class_id = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mimics class inheritance
|
* Mimics class inheritance
|
||||||
*
|
*
|
||||||
|
@ -274,18 +276,20 @@ var extend = ( function( extending )
|
||||||
// set up the new class
|
// set up the new class
|
||||||
var new_class = createCtor( abstract_methods );
|
var new_class = createCtor( abstract_methods );
|
||||||
|
|
||||||
setupProps( new_class, abstract_methods );
|
|
||||||
attachPropInit( prototype, properties );
|
attachPropInit( prototype, properties );
|
||||||
|
|
||||||
new_class.prototype = prototype;
|
new_class.prototype = prototype;
|
||||||
new_class.constructor = new_class;
|
new_class.constructor = new_class;
|
||||||
|
|
||||||
|
// important: call after setting prototype
|
||||||
|
setupProps( new_class, abstract_methods, ++class_id );
|
||||||
|
|
||||||
// lock down the new class (if supported) to ensure that we can't add
|
// lock down the new class (if supported) to ensure that we can't add
|
||||||
// members at runtime
|
// members at runtime
|
||||||
util.freeze( new_class );
|
util.freeze( new_class );
|
||||||
|
|
||||||
// create internal metadata for the new class
|
// create internal metadata for the new class
|
||||||
createMeta( new_class );
|
createMeta( new_class, base.prototype.__cid );
|
||||||
|
|
||||||
// we're done with the extension process
|
// we're done with the extension process
|
||||||
extending = false;
|
extending = false;
|
||||||
|
@ -360,13 +364,15 @@ var extend = ( function( extending )
|
||||||
*
|
*
|
||||||
* @param {Function} func function (class) to set up
|
* @param {Function} func function (class) to set up
|
||||||
* @param {Array.<string>} abstract_methods list of abstract method names
|
* @param {Array.<string>} abstract_methods list of abstract method names
|
||||||
|
* @param {number} class_id unique id to assign to class
|
||||||
*
|
*
|
||||||
* @return {undefined}
|
* @return {undefined}
|
||||||
*/
|
*/
|
||||||
function setupProps( func, abstract_methods )
|
function setupProps( func, abstract_methods, class_id )
|
||||||
{
|
{
|
||||||
attachAbstract( func, abstract_methods );
|
attachAbstract( func, abstract_methods );
|
||||||
attachExtend( func );
|
attachExtend( func );
|
||||||
|
attachId( func, class_id );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -466,6 +472,25 @@ function attachExtend( func )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches the unique id to the class and its prototype
|
||||||
|
*
|
||||||
|
* The unique identifier is used internally to match a class and its instances
|
||||||
|
* with the class metadata. Exposing the id breaks encapsulation to a degree,
|
||||||
|
* but is a lesser evil when compared to exposing all metadata.
|
||||||
|
*
|
||||||
|
* @param {Function} func function (class) to attach method to
|
||||||
|
* @param {number} id id to assign
|
||||||
|
*
|
||||||
|
* @return {undefined}
|
||||||
|
*/
|
||||||
|
function attachId( func, id )
|
||||||
|
{
|
||||||
|
util.defineSecureProp( func, '__cid', id );
|
||||||
|
util.defineSecureProp( func.prototype, '__cid', id );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attaches partially applied isInstanceOf() method to class instance
|
* Attaches partially applied isInstanceOf() method to class instance
|
||||||
*
|
*
|
||||||
|
@ -488,15 +513,30 @@ function attachInstanceOf( instance )
|
||||||
/**
|
/**
|
||||||
* Initializes class metadata for the given class
|
* Initializes class metadata for the given class
|
||||||
*
|
*
|
||||||
* @param {Class} obj class to initialize metadata for
|
* @param {Class} func class to initialize metadata for
|
||||||
*
|
*
|
||||||
* @return {undefined}
|
* @return {undefined}
|
||||||
*/
|
*/
|
||||||
function createMeta( obj )
|
function createMeta( func, parent_id )
|
||||||
{
|
{
|
||||||
class_meta[ obj ] = {
|
var id = func.__cid,
|
||||||
implemented: [],
|
parent_meta = ( ( parent_id ) ? getMeta( parent_id) : undefined );
|
||||||
};
|
|
||||||
|
// copy the parent prototype's metadata if it exists (inherit metadata)
|
||||||
|
if ( parent_meta )
|
||||||
|
{
|
||||||
|
// todo: deep (1) util.clone() when available
|
||||||
|
class_meta[ id ] = {
|
||||||
|
implemented: util.clone( parent_meta.implemented ),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// create empty
|
||||||
|
class_meta[ id ] = {
|
||||||
|
implemented: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -506,12 +546,12 @@ function createMeta( obj )
|
||||||
* Since a reference is returned (rather than a copy), the returned object can
|
* Since a reference is returned (rather than a copy), the returned object can
|
||||||
* be modified to alter the metadata.
|
* be modified to alter the metadata.
|
||||||
*
|
*
|
||||||
* @param {Class} obj class to retrieve metadata for
|
* @param {number} id id of class to retrieve metadata for
|
||||||
*
|
*
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
function getMeta( obj )
|
function getMeta( id )
|
||||||
{
|
{
|
||||||
return class_meta[ obj ];
|
return class_meta[ id ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,3 +77,21 @@ assert.ok(
|
||||||
"Abstract methods list contains names of implemented methods"
|
"Abstract methods list contains names of implemented methods"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// concrete implementation so that we can instantiate it
|
||||||
|
var ConcreteFoo = Foo.extend(
|
||||||
|
{
|
||||||
|
foo: function() {},
|
||||||
|
foo2: function() {},
|
||||||
|
}),
|
||||||
|
|
||||||
|
concrete_inst = new ConcreteFoo();
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
( concrete_inst.isInstanceOf( Type )
|
||||||
|
&& concrete_inst.isInstanceOf( Type2 )
|
||||||
|
),
|
||||||
|
"Instances of classes implementing interfaces are considered to be " +
|
||||||
|
"instances of the implemented interfaces"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
|
@ -132,3 +132,15 @@ assert.equal(
|
||||||
"Class instance contains isA() alias for isInstanceOf() partially applied function"
|
"Class instance contains isA() alias for isInstanceOf() partially applied function"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
( Foo.__cid !== undefined ),
|
||||||
|
"Class id available via class"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
( Foo.prototype.__cid !== undefined ),
|
||||||
|
"Class id available via class prototype"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue