This is an example of extending JavaScript native Array
object with our own functions (methods), written in CoffeeScript:
Array::add = (mod) -> @map (val) -> val + mod
Array::mult = (mod) -> @map (val) -> val * mod
Array::div = (mod) -> @map (val) -> val / mod
Array::step = (step) -> i for i in @ by step
Array::fill = (val, len) -> val for i in (
if len
then [0...len]
else if @length == 1
then [0...@[0]]
else @
)
Then we can use them like this:
[0, 1, 2].add(2) # [2, 3, 4]
[0, 1, 2].mult(2) # [0, 2, 4]
[0, 1, 2].add(2).mult(2) # [4, 6, 8]
[0, 2, 4].div(2) # [0, 1, 2]
# ranges - dots matter!
[0...2] # [0, 1]
[0..2] # [0, 1, 2]
[0..2].add(2) # [2, 3, 4]
[0..2].mult(2) # [0, 2, 4]
[0..2].add(2).mult(2) # [4, 6, 8]
[0..2].fill(0) # [0, 0, 0]
[0..4].step(2) # [0, 2, 4]
[0..4].step(2).fill(0) # [0, 0, 0]
# fill specifics
[].fill(0) # []
[].fill(0, 3) # [0, 0, 0]
[3].fill(0) # [0, 0, 0] - instead of just [0]
One can rename step
to by
- to make it look similar to for
loop with "by step", but I just put more meaningful name.
And it works absolutely fine in plain CoffeeScript, but not in our Ship Editor.
Unfortunately Ship Editor has a bug, because of which you won't be able to make a Mod Export of your ship after adding these useful functions. But luckily, there are plenty of options to fix it nicely. I'll show you some of the most worthy.
There are two main types of fixes: post-fix and pre-fix, and two sub-types: simple-straightforward and universal-reliable.
All fixes are made in such a way that no matter how many functions you add in the future for Array
, fixes will continue to work and nothing needs to be corrected.
It's when uwu oopsie woopsie we made a fucky wucky or ewen fucko boingo already, and now we wanna fix it after an incident. You should put this fix after extending Array
with your functions.
The main advantage of this way is that we can keep original syntax of Array
extending. E.g. if someone got used to it or is going to use it elsewhere.
This method is about fixing one particular error, which happens currently in Ship Editor during Mod Export (and some other internal tasks). It won't help anymore, once after some Ship Editor update we might get more kind of errors.
But it's pretty simple! And works now.
for key of []
Array::[key].toFixed = ->
This one is a complex and smart solution, which will make all our custom functions look similar to native Array
methods. Ship Editor will always be happy enough to never produce an error because of them, no matter how many updates he gets in the future (if ever, welp).
for key, value of []
delete Array::[key]
Object.defineProperty Array::, key,
value: value
configurable: true
enumerable: false
But since it's a post-fix and we already made an oopsie woopsie - it has to delete
our fucko-wucko first, and only then it can go further to re-add our functions more covertly.
That's why here is the next section.
So maybe one don't wanna oopsie woopsie at all? Here is mighty pre-paration actions in advance, which will help you avoid even creation of "bad" functions (despite they could be fixed a moment later using post-fix). You should put this fix before extending Array
with your functions.
But also with the main drawback - the loss of the classic syntax of extending. So it's with usage examples of extending here.
Remember, only against current particular error.
Array.extend = (properties) ->
for own key, value of properties
@::[key] = value
@::[key].toFixed = ->
Array.extend
add: (mod) -> @map (val) -> val + mod
mult: (mod) -> @map (val) -> val * mod
And finally, maybe the most interesting methods, since both reliable and preliminary.
I will show you 3 options, with a slightly different usage syntax of extending - pay attention mainly to it:
Array.extend = (properties) ->
for own key, value of properties
Object.defineProperty @::, key,
value: value
configurable: true
enumerable: false
Array.extend
add: (mod) -> @map (val) -> val + mod
mult: (mod) -> @map (val) -> val * mod
extend = (type, properties) ->
for own key, value of properties
Object.defineProperty type::, key,
value: value
configurable: true
enumerable: false
extend Array,
add: (mod) -> @map (val) -> val + mod
mult: (mod) -> @map (val) -> val * mod
extend = (descriptor) ->
for own type, properties of descriptor
for own key, value of properties
Object.defineProperty @[type]::, key,
value: value
configurable: true
enumerable: false
extend Array:
add: (mod) -> @map (val) -> val + mod
mult: (mod) -> @map (val) -> val * mod
Another difference is that the last two options allow you to extend other types/objects besides Array
. And the last one - even inside one shared call:
extend
Array:
add: -> # some function
mult: -> # some function
Object:
merge: -> # some function
String:
oof: -> # some function
Number:
welp: -> # some function
But most probably you won't ever need this, because for other native types JavaScript/CoffeeScript have enough native methods you could use for ship building. So, just choose by preferred syntax. Although, who knows the future...
Also there are some possible optimizations, but they are just unnecessary code complications here because this code runs only once after compilation, works pretty fast and with a very small number of extending functions
(totally not some 1e100 cycles).