

Args.js – Optional and Default parameters for JavaScript - adam-a
http://autographer.github.io/args.js/

======
callum85
Big caveat: this doesn't work in strict mode.

[https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode/Transitioning_to_strict_mode#Poisoned_arguments_and_function_properties)

~~~
Touche
Just comment out the line that uses caller and always pass your arguments.

~~~
adam-a
I was unaware of the strict mode problem. I never use it myself, probably
because I'm not developing for the web, but a browser embedded in a desktop
app.

I will have a look at providing a version that works in strict mode.

~~~
adam-a
I have just updated to remove the Function.caller and added a strict mode
declaration.

~~~
callum85
Awesome! I am definitely going to use this now. Been looking for something
like this for a while.

------
phase_9
I've seen this style of library come up quite a few times whilst hacking away
in JavaScript where developers try to introduce a sense of strict typing (I
even wrote a similar one myself when I first moved over from ActionScript). In
the end I learned to stop worrying and love the <strike>bomb</strike> duck
typing[0].

For me; one of the "joys" of coding JavaScript is the expressiveness that
comes with a dynamic languages; should you throw an ArgumentError when your
function that expects a number is invoked with a String? Maybe - sure it can
help catch problems early and effectively "stop-the-line" in your public API,
but then again it will probably end up throwing the classic `TypeError: Object
foo has no method 'bar'` for you anyway.

For "public" methods which form part of an API (especially when that API is
going to be shared outside of my team) I try to make my functions handle
failure early (before they delegate off to the "private" internal methods),
even better if the public methods can repair any unexpected usage, ie:

    
    
      function convertToHex(value) {
        var result;
        
        if (typeof value !== "number") {
          result = convertToHex(parseInt(value, 10));
        }
        else {
          result = "0x" + value.toString(16);
        }
        
        return result;
      }
    

Also, with regards to default argument values, I've always felt the "native"
JavaScript approach was fairly compact and descriptive when required:

    
    
      function doFoo(bar) {
        bar = (bar !== undefined) ? bar : "default_value";
      }
    
    

[0]
[http://en.wikipedia.org/wiki/Duck_typing](http://en.wikipedia.org/wiki/Duck_typing)

~~~
simon_renoult
Why using :

    
    
        function doFoo(bar) {
          bar = (bar !== undefined) ? bar : "default_value";
        }
    

Over :

    
    
        function doFoo(bar) {
          bar = bar || "default_value";
        }
    

Coercion avoidance ?

~~~
1wheel
0 is falsy in javascript; with the second method, default_value will get used
instead.

~~~
elclanrs
I'd use:

    
    
      bar = bar != null ? bar : 'default';

------
curveship
'Args.caller.arguments' gives me the willies. That's a non-standardized
extension combined with a deprecated one [0][1]. I'd have to be itching quite
a lot to scratch it like this.

0 - [https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller)

1 - [https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/Function/arguments)

~~~
prezjordan
Is there an alternative that provides the same functionality?

~~~
curveship
Not cleanly. You have to pass the arguments in explicitly, and you also need
to convert them to an array (the arguments object is not an array [only array-
like] and it's not supposed to escape its function body). So you need to do:

var args = Args([ ... defs ... ], Array.prototype.slice.call(arguments));

~~~
adam-a
I have just updated the code to remove the Function.caller.arguments part,
this means you have to pass through the arguments variable manually.

The arguments object is not a real array, this is true, but it has a length
and is indexable, so Args.js still works ok.

I have seen on MDN[0]:

> The arguments object is available only within a function body. Attempting to
> access the arguments object outside a function declaration results in an
> error.

It doesn't actually cause an error though, so am I misreading that? Besides,
by that definition passing arguments to Array.prototype.slice.call() is also a
no-no.

[0] [https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments)

~~~
curveship
The ES5 spec specifically allows (but doesn't require) implementations to
create an Array.prototype.slice that accepts 'arguments':

> NOTE: The slice function is intentionally generic; it does not require that
> its this value be an Array object. Therefore it can be transferred to other
> kinds of objects for use as a method. _Whether the slice function can be
> applied successfully to a host object [ed: 'arguments' is one such] is
> implementation-dependent._ [0]

'arguments' isn't just array-like. It's got some very weird behavior. For
instance, if your function also has named parameters, and you set an index on
the arguments object that corresponds with a parameter, then it changes the
value of the local parameter as well.

0 - [http://es5.github.io/#x15.4.4.10](http://es5.github.io/#x15.4.4.10)

------
nebulous1
Appears to use Function.caller ( [https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller) )

"Non-standard This feature is non-standard and is not on a standards track. Do
not use it on production sites facing the Web: it will not work for every
user. There may also be large incompatibilities between implementations and
the behavior may change in the future."

~~~
gnud
It seems like you can avoid using Function.caller by suppling Args.js with the
arguments yourself, like

Args([/* argument definition */], arguments);

~~~
adam-a
That's right. The Function.caller bit was only added in quite late and the
library works just as well without it, although you have to type a bit more.

------
gosukiwi
Wouldn't sweet.js and a macro make a more eye appealing solution? The idea is
nice and all but the syntax... is just so ugly.

~~~
acjohnson55
I was thinking the same thing.

That and I'm not understanding from the page how this is used in the first
place. Is the `arguments` implicit variable being passed in some place? (EDIT:
yes, via `Function.caller`.) Are the argument values accessed through the
result of calling `Args`?

For a non-macro implementation of this, it might be interesting to use
function decorator syntax instead, to put the argument processing _outside_
the function body entirely. Basically, the argument processor decorator
function would take an options array and a target function, which has all
possible parameters, explicitly named. The decorator would spit out a new
function that invisibly does all of the wrangling of `arguments` and then
finally calls the target function. An advantage is that I think this would be
the most minimal pure JS solution from a syntax perspective, and it would keep
the actual function body uncluttered. A disadvantage is that it could only
really be used with function expressions (not declarations).

~~~
adam-a
I'm really excited about sweet.js and JS macros in general, but I haven't had
a chance to dive into anything yet. The major disadvantage for me though is it
adds a pre-processing step.

~~~
acjohnson55
I think resistance to pre-processing is futile. Most people already use it for
minification at the very least. With things like brunch and grunt, pre-
processing can be pretty much transparent during development. I actually don't
know what sweet.js's sourcemap story is, but potentially transparency could
more or less be achieved even during debugging. Maybe we'll eventually even
get native JS macro language support!

------
andrey-p
I created something very similar recently, less features but more lightweight.
Didn't plan to publicise it but I thought it's relevant:

[https://github.com/andrey-p/arg-err](https://github.com/andrey-p/arg-err)

------
jdc0589
Seen lots of these at this point. I still don't really understand their
purpose.

Its not hard, and doesn't require much, if any, more code than the config
options for libraries like this to just handle your arguments normally.

required arguments get defined in your function signature. You assign optional
args to local variables defined within your function body via the arguments
object. It's pretty easy.

~~~
adam-a
The original motivation was providing a single way to deal with a combination
of default arguments, optional arguments and the sort of homebrew polymorphism
that a lot of libraries use, where a function takes 4 different types of thing
and runs through a bunch type tests before doing anything.

For functions with longer parameter lists this should hopefully be more
readable too. It's clear what types can be passed to a function.

Out of interest, what other libraries like this have you seen? I did a quick
check myself and couldn't find anything comparable.

------
K0nserv
I experimented with something similar a while ago, however my main focus was
strictness and not default and optional parameters. I did end up adding it
though.

[https://github.com/k0nserv/Strict.js](https://github.com/k0nserv/Strict.js)

~~~
zephjc
typo here in your readme:

    
    
        this.define('isFemale', female, String.Boolean);
    

should be

    
    
        this.define('isFemale', female, Strict.Boolean);

~~~
K0nserv
Will fix, thanks for pointing it out.

------
muskalek
Or you can just use Coffeescript.
[http://coffeescript.org/#literals](http://coffeescript.org/#literals)

~~~
Rygu
You're missing the validation parts.

------
michaelwww
TypeScript works for me and it doesn't require any additional JS code.
Granted, it won't do runtime checks.

------
abimaelmartell
nice idea but ugly sintax :S

