diff --git a/lib/interface.js b/lib/interface.js index 0417483..7a41687 100644 --- a/lib/interface.js +++ b/lib/interface.js @@ -45,40 +45,70 @@ exports.extend = function() function Interface() {} -function extend() +var extend = ( function( extending ) { - var args = Array.prototype.slice.call( arguments ), - props = args.pop() || {}, - base = args.pop() || Interface, - prototype = new base(); + return function extend() + { + // ensure we'll be permitted to instantiate interfaces for the base + extending = true; - // sanity check - inheritCheck( prototype ); + var args = Array.prototype.slice.call( arguments ), + props = args.pop() || {}, + base = args.pop() || Interface, + prototype = new base(); - var new_interface = function() {}; + // sanity check + inheritCheck( prototype ); - util.propCopy( props, prototype, { - each: function( name, value ) - { - if ( util.isAbstractMethod( value ) === false ) + var new_interface = createInterface(); + + util.propCopy( props, prototype, { + each: function( name, value ) { - throw TypeError( - "Only abstract methods are permitted within Interface " + - "definitons" - ); + if ( util.isAbstractMethod( value ) === false ) + { + throw TypeError( + "Only abstract methods are permitted within Interface " + + "definitons" + ); + } + }, + } ); + + attachExtend( new_interface ); + new_interface.prototype = prototype; + new_interface.constructor = new_interface; + + // freeze the interface (preventing additions), if supported + util.freeze( new_interface ); + + // we're done; let's not allow interfaces to be instantiated anymore + extending = false; + + return new_interface; + }; + + + /** + * Creates a new interface constructor function + * + * @return {function()} + */ + function createInterface() + { + return function() + { + // allows us to extend the interface without throwing an exception + // (since the prototype requires an instance) + if ( !extending ) + { + // only called if someone tries to create a new instance of an + // interface + throw Error( "Interfaces cannot be instantiated" ); } - }, - } ); - - attachExtend( new_interface ); - new_interface.prototype = prototype; - new_interface.constructor = new_interface; - - // freeze the interface (preventing additions), if supported - util.freeze( new_interface ); - - return new_interface; -} + }; + } +} )( false ); /** diff --git a/test/test-interface-extend.js b/test/test-interface-extend.js index efa3236..7573536 100644 --- a/test/test-interface-extend.js +++ b/test/test-interface-extend.js @@ -75,7 +75,7 @@ var SubType = Interface.extend( BaseType, }); assert.ok( - ( new SubType() instanceof BaseType ), + ( SubType.prototype instanceof BaseType ), "Generic interface extend method can extend from other interfaces" ); @@ -102,7 +102,7 @@ var SubType2 = BaseType.extend( }); assert.ok( - ( new SubType2 instanceof BaseType ), + ( SubType2.prototype instanceof BaseType ), "Interface extend method can extend interfaces" ); diff --git a/test/test-interface.js b/test/test-interface.js index 5cc8f2e..0236940 100644 --- a/test/test-interface.js +++ b/test/test-interface.js @@ -35,6 +35,12 @@ assert.ok( "Interface extend method creates a new interface object" ); +assert.throws( function() +{ + new FooType(); +}, Error, "Interfaces cannot be instantiated" ); + + // only perform check if supported by the engine if ( Object.isFrozen ) {