Extend class and pass all constructor arguments to super
How would you extend a class using CoffeeScript, but have the construction 开发者_如何学运维arguments passed to super?
Eg:
class List extends Array
# Some other stuff to make it work...
list = new List(1,2,3)
console.log list
[1, 2, 3]
In general, this would work without additional code; the parent constructor is used unless expressly overridden:
class A
constructor: ->
console.log arg for arg in arguments
class B extends A
new B('foo') # output: 'foo'
And the problem isn't that Array doesn't have a constructor
method:
coffee> Array.constructor
[Function: Function]
The problem is just that Array
is just plain weird. While arrays are "just objects" in principle, in practice they're stored differently. So when you try to apply that constructor to an object that isn't an array (even if it passes the instanceof Array
test), it doesn't work.
So, you can use Acorn's solution, but then you may run into other problems down the road (especially if you pass a List
to something that expects a true array). For that reason, I'd recommend implementing List
as a wrapper around an array instance, rather than trying to use inheritance from a native object type.
While we're on the subject, one very important clarification: When you use super
by itself, that does pass all arguments! This behavior is borrowed from Ruby. So
class B extends A
constructor: ->
super
will pass along all arguments to A
's constructor, while
class B extends A
constructor: ->
super()
will invoke A
's constructor with no arguments.
class List extends Array
constructor: ->
@push arguments...
toString: ->
@join('-')
list = new List(1, 2)
list.push(3)
list.toString()
=>
'1-2-3'
Using extends
in CoffeeScript expects the superclass to be in CoffeeScript too. If you're using a non-CS class, e.g. Array
in the original question, then you may encounter problems.
This solved the general case for me. It's a bit of a hack because it uses _super
which probably isn't intended to be used in the compiled JS.
class MyClass extends SomeJsLib
constructor: ->
_super.call @, arg1, arg2
Or if you just want to pass thru arguments from the caller:
class MyClass extends SomeJsLib
constructor: ->
_super.apply @, arguments
In my exploration of javascript I required a generic way to create a class with a dynamic number of constructor arguments. As mentioned this won't work for array as far as I know, it will only work for coffee-script style classes.
Calling a specific function with a dynamic number of arguments is easy enough through .apply
args = [1, 2, 3]
f = measurement.clone
f.apply measurement, args
A class can extend a class saved in a variable. As a result we can write a function that returns new sub-classes.
classfactory = (f) ->
class extension extends f
Putting it all together we can create a function that returns new sub-classes in which we apply
arguments to the super class's constructor.
classwitharguments = (f, args) ->
class extension extends f
constructor: () ->
extension.__super__.constructor.apply @, args
To use this new factory
args = [1, 2, 3]
instance = new (classwitharguments Measurement, args)
Thoughts? Comments? Suggestions? Limitations I didn't think about? Let me know.
精彩评论