Hacker News new | comments | show | ask | jobs | submit login
Args.js – Optional and Default parameters for JavaScript (autographer.github.io)
53 points by adam-a 1411 days ago | hide | past | web | favorite | 39 comments



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

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...


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


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.


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


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


'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...

1 - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...


Is there an alternative that provides the same functionality?


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));


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...


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


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

"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."


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

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


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.


It might not be on the standards track, but the browser support table at the bottom seems to indicate that this is a de-facto standard.


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.


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).


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.


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!


Not necessarily, sweet.js runs in the browser and there is a plugin for Requirejs so it can be transparent to you during development.


More than that, macros would be a static solution, expanding to sensible arguments parsing code at compile time. So much metaprogramming these days is dynamic where you pay excessive interpretive cost to evaluate the little DSL every time the function is called. JavaScript programmers are guilty of this, but not nearly as guilty as Ruby programmers...


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


I'm not sure that embracing a languages dynamic nature should get in the way of leveraging libraries to write more readable code. One advantage of using a standard approach (be it a third party library, or something bespoke) is that you could enable / disable the 'type checking' depending on the build environment. I wouldn't be surprised if your existing toolchain isn't already bringing some static inference to your development - are you using jsdoc and an IDE with autocomplete, for example?

Trusting you find Dart, Typescript, et al agreeable you have to concede that some people are working with legacy code and can only make iterative changes to their codebase?


Why using :

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

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


The second function sets bar to "default_value" if called with a falsy argument like 0 or an empty string.


Yes, the second option is better in most cases. I'd spend more time reading the first one to understand what is going on compared to the second option.

Keep in mind that you will have to go for the first option (or other better options) if you expect 'falsy' values to be passed as arguments.


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


I'd use:

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


For me it was partly about providing type errors, but also very much about providing easy to write and easy to read type testing and sorting. Type testing in javascript can be a bit tough on the eye and this hopefully makes it easy to see what parameter types a function will accept.


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


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.


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.


Another good reason to assign everything to a local variable ASAP is that if I leave everything in the args object then jshint can't help me prevent typos when accessing fields from that object.


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


typo here in your readme:

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

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


Will fix, thanks for pointing it out.


Or you can just use Coffeescript. http://coffeescript.org/#literals


You're missing the validation parts.


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


nice idea but ugly sintax :S




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | DMCA | Apply to YC | Contact

Search: