Preparing to remove abstractMethods from public prototype; now uses hash for performance since it no longer needs to be referenced cleanly externally
- It will be later referenced via reflectionclosure/master
parent
069ef6717f
commit
881edc0cc6
26
lib/class.js
26
lib/class.js
|
@ -217,16 +217,10 @@ var extend = ( function( extending )
|
|||
members = member_builder.initMembers(
|
||||
prototype, prototype, prototype
|
||||
),
|
||||
abstract_methods = ( base.abstractMethods || [] ).slice();
|
||||
|
||||
// it's much faster to lookup a hash than it is to iterate through an
|
||||
// entire array each time we need to find an existing abstract method
|
||||
var abstract_map = {};
|
||||
for ( var i = 0, len = abstract_methods.length; i < len; i++ )
|
||||
{
|
||||
var method = abstract_methods[ i ];
|
||||
abstract_map[ method ] = i;
|
||||
}
|
||||
abstract_methods = util.clone( base.abstractMethods )
|
||||
|| { __length: 0 }
|
||||
;
|
||||
|
||||
util.propParse( props, {
|
||||
each: function( name, value, keywords )
|
||||
|
@ -269,15 +263,17 @@ var extend = ( function( extending )
|
|||
|
||||
if ( is_abstract )
|
||||
{
|
||||
abstract_methods.push( name );
|
||||
abstract_methods[ name ] = true;
|
||||
abstract_methods.__length++;
|
||||
}
|
||||
else if ( ( abstract_map[ name ] !== undefined )
|
||||
else if ( ( abstract_methods[ name ] )
|
||||
&& ( is_abstract === false )
|
||||
)
|
||||
{
|
||||
// if this was a concrete method, then it should no longer
|
||||
// be marked as abstract
|
||||
delete abstract_methods[ abstract_map[ name ] ];
|
||||
delete abstract_methods[ name ];
|
||||
abstract_methods.__length--;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -289,8 +285,6 @@ var extend = ( function( extending )
|
|||
},
|
||||
} );
|
||||
|
||||
abstract_methods = util.arrayShrink( abstract_methods );
|
||||
|
||||
// reference to the parent prototype (for more experienced users)
|
||||
prototype.parent = base.prototype;
|
||||
|
||||
|
@ -332,7 +326,7 @@ var extend = ( function( extending )
|
|||
function createCtor( abstract_methods )
|
||||
{
|
||||
// concrete class
|
||||
if ( abstract_methods.length === 0 )
|
||||
if ( abstract_methods.__length === 0 )
|
||||
{
|
||||
var args = null;
|
||||
|
||||
|
@ -507,7 +501,7 @@ function attachPropInit( prototype, properties )
|
|||
*/
|
||||
function attachAbstract( func, methods )
|
||||
{
|
||||
var is_abstract = ( methods.length > 0 ) ? true: false;
|
||||
var is_abstract = ( methods.__length > 0 ) ? true: false;
|
||||
|
||||
/**
|
||||
* Returns whether the class contains abstract methods (and is therefore
|
||||
|
|
|
@ -72,11 +72,6 @@ assert.ok(
|
|||
"All classes should have an isAbstract() method"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
( Foo.abstractMethods instanceof Array ),
|
||||
"All classes should provide a list of their abstract methods as an array"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
Foo.isAbstract(),
|
||||
false,
|
||||
|
@ -96,28 +91,6 @@ assert.equal(
|
|||
"concrete implementation for all abstract methods"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
( ( AbstractFoo.abstractMethods[ 0 ] == 'method' )
|
||||
&& ( AbstractFoo.abstractMethods[ 1 ] == 'second' )
|
||||
),
|
||||
"Abstract classes should provide a list of their abstract methods' names"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
( ( SubAbstractFoo.abstractMethods[ 0 ] == 'method' )
|
||||
&& ( SubAbstractFoo.abstractMethods[ 1 ] == undefined )
|
||||
),
|
||||
"Abstract subclasses should not have their concrete methods within the " +
|
||||
"the abstract method list if the concrete method was abstract in " +
|
||||
"its supertype"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
ConcreteFoo.abstractMethods.length,
|
||||
0,
|
||||
"Concrete classes should not have any abstract methods listed"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
ConcreteFoo.isAbstract(),
|
||||
false,
|
||||
|
|
|
@ -112,6 +112,11 @@ var Type = Interface.extend( {
|
|||
} )();
|
||||
|
||||
|
||||
/**
|
||||
* Since interfaces can contain only abstract methods, it stands to reason that
|
||||
* any class implementing an interface without providing any concrete methods
|
||||
* should be abstract by default.
|
||||
*/
|
||||
( function testClassesImplementingInterfacesAreConsideredAbstractByDefault()
|
||||
{
|
||||
assert.equal(
|
||||
|
@ -123,40 +128,6 @@ var Type = Interface.extend( {
|
|||
} ) ();
|
||||
|
||||
|
||||
( function testAbstractMethodListUpdatedWhenInterfaceImplemented()
|
||||
{
|
||||
// no base
|
||||
assert.equal(
|
||||
Foo.abstractMethods.length,
|
||||
2,
|
||||
"Abstract methods list is updated when interface is implemented " +
|
||||
"(empty base)"
|
||||
);
|
||||
|
||||
// PlainFoo base
|
||||
assert.equal(
|
||||
PlainFoo2.abstractMethods.length,
|
||||
2,
|
||||
"Abstract methods list is updated when interface is implemented " +
|
||||
"(concrete base)"
|
||||
);
|
||||
} )();
|
||||
|
||||
|
||||
( function testProperAbstractMethodsAreCopiedFromInterface()
|
||||
{
|
||||
assert.ok(
|
||||
( ( Foo.abstractMethods[ 0 ] == 'foo' )
|
||||
&& ( Foo.abstractMethods[ 1 ] == 'foo2' )
|
||||
)
|
||||
&& ( ( PlainFoo2.abstractMethods[ 0 ] == 'foo' )
|
||||
&& ( PlainFoo2.abstractMethods[ 1 ] == 'foo2' )
|
||||
),
|
||||
"Abstract methods list contains names of implemented methods"
|
||||
);
|
||||
} )();
|
||||
|
||||
|
||||
( function testInstancesOfClassesAreInstancesOfTheirImplementedInterfaces()
|
||||
{
|
||||
// concrete implementation so that we can instantiate it
|
||||
|
@ -175,5 +146,11 @@ var Type = Interface.extend( {
|
|||
"Instances of classes implementing interfaces are considered to be " +
|
||||
"instances of the implemented interfaces"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
ConcreteFoo.isAbstract(),
|
||||
false,
|
||||
"Concrete implementations are not considered to be abstract"
|
||||
);
|
||||
} )();
|
||||
|
||||
|
|
Loading…
Reference in New Issue