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(
|
members = member_builder.initMembers(
|
||||||
prototype, prototype, prototype
|
prototype, prototype, prototype
|
||||||
),
|
),
|
||||||
abstract_methods = ( base.abstractMethods || [] ).slice();
|
|
||||||
|
|
||||||
// it's much faster to lookup a hash than it is to iterate through an
|
abstract_methods = util.clone( base.abstractMethods )
|
||||||
// entire array each time we need to find an existing abstract method
|
|| { __length: 0 }
|
||||||
var abstract_map = {};
|
;
|
||||||
for ( var i = 0, len = abstract_methods.length; i < len; i++ )
|
|
||||||
{
|
|
||||||
var method = abstract_methods[ i ];
|
|
||||||
abstract_map[ method ] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
util.propParse( props, {
|
util.propParse( props, {
|
||||||
each: function( name, value, keywords )
|
each: function( name, value, keywords )
|
||||||
|
@ -269,15 +263,17 @@ var extend = ( function( extending )
|
||||||
|
|
||||||
if ( is_abstract )
|
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 )
|
&& ( is_abstract === false )
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// if this was a concrete method, then it should no longer
|
// if this was a concrete method, then it should no longer
|
||||||
// be marked as abstract
|
// 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)
|
// reference to the parent prototype (for more experienced users)
|
||||||
prototype.parent = base.prototype;
|
prototype.parent = base.prototype;
|
||||||
|
|
||||||
|
@ -332,7 +326,7 @@ var extend = ( function( extending )
|
||||||
function createCtor( abstract_methods )
|
function createCtor( abstract_methods )
|
||||||
{
|
{
|
||||||
// concrete class
|
// concrete class
|
||||||
if ( abstract_methods.length === 0 )
|
if ( abstract_methods.__length === 0 )
|
||||||
{
|
{
|
||||||
var args = null;
|
var args = null;
|
||||||
|
|
||||||
|
@ -507,7 +501,7 @@ function attachPropInit( prototype, properties )
|
||||||
*/
|
*/
|
||||||
function attachAbstract( func, methods )
|
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
|
* Returns whether the class contains abstract methods (and is therefore
|
||||||
|
|
|
@ -72,11 +72,6 @@ assert.ok(
|
||||||
"All classes should have an isAbstract() method"
|
"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(
|
assert.equal(
|
||||||
Foo.isAbstract(),
|
Foo.isAbstract(),
|
||||||
false,
|
false,
|
||||||
|
@ -96,28 +91,6 @@ assert.equal(
|
||||||
"concrete implementation for all abstract methods"
|
"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(
|
assert.equal(
|
||||||
ConcreteFoo.isAbstract(),
|
ConcreteFoo.isAbstract(),
|
||||||
false,
|
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()
|
( function testClassesImplementingInterfacesAreConsideredAbstractByDefault()
|
||||||
{
|
{
|
||||||
assert.equal(
|
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()
|
( function testInstancesOfClassesAreInstancesOfTheirImplementedInterfaces()
|
||||||
{
|
{
|
||||||
// concrete implementation so that we can instantiate it
|
// 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 classes implementing interfaces are considered to be " +
|
||||||
"instances of the implemented interfaces"
|
"instances of the implemented interfaces"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
ConcreteFoo.isAbstract(),
|
||||||
|
false,
|
||||||
|
"Concrete implementations are not considered to be abstract"
|
||||||
|
);
|
||||||
} )();
|
} )();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue