Javascript notes - unsorted

From Helpful
Jump to: navigation, search
Related to web development, lower level hosting, and such: (See also the webdev category)

Lower levels


Server stuff:


Higher levels


this

'this' is bound to different things in different context, which seem a little magic at first.

For example:

  • In object members,
    this
    is the object
  • of functions used as event handlers,
    this
    is the DOM element they are set on
but there are details, see events
  • in nonmethod functions,
    this
    is the global object (in browsers meaning window)
...but not in strict mode
  • ...more?

See http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Operators:Special_Operators:this_Operator


strict mode

This article/section is a stub — probably a pile of half-sorted notes, is not well-checked so may have incorrect bits. (Feel free to ignore, or tell me)


ES5 introduced a strict mode you can opt into (not doing so is informally called sloppy mode).

strict mode most meant stricter handling of certain syntax (deprecated some)

throwing exceptions for some things that would previously be silently ignored (enforcing some better coding practices in programmers),

It also made it easier to introduce new syntax.


You can declare each script to be in strict mode, by having the string 'use strict'; be the first expression in it.

Anything else is in sloppy mode.


Imported modules are implicitly in strict mode, even without that.


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

http://speakingjs.com/es5/ch07.html#strict_mode

asynchronous coding

Javascript is single-threaded in its language design, so execution is event loop style, multitasking has to be cooperative, you want to split up longer tasks.

It's good habit to do this explicitly for anything that has multiple tasks that have to stay responsive.



Callback style

Say you have an UI that triggers what comes down to:

collect_form();
interact_server();
update_page();

...with no data passed - you can, but not doing so keeps the example simpler.

Doing it in three successive calls like that in a scope will mean nothing else can execute until all that is completely done. In the browser, the only times the UI could update would probably before and after, not between these calls.


A basic improvement is to schedule it in three parts, by having each function queue the next as soon as possible, by using setTimeout rather than calling the next function directly.

One way to do that would be to hardcode that:

function collect_form()    { console.log('collect_form, then');    setTimeout(interact_server,0) }
function interact_server() { console.log('interact_server, then'); setTimeout(update_page,0)}
function update_page()     { console.log('update_page'); }
 
collect_form()


It's somewhat awkward in that

it's not composable - these functions are really hardcoded parts of the sequence, just split up for interaction's/pretense's sake.
You'll just see something called 'collect_form' and have to dig deeper to see what that code ends up doing. When debugging you'll be chasing code paths around. Naming helps a little, but only so much.
other issues we'll mention later


You could hand in the callback functions, like

function collect_form(callback)    { console.log('collect_form, then');      setTimeout(callback,0) }
function interact_server(callback) { console.log('interact_server, then');   setTimeout(callback,0) }
function update_page()             { console.log('update_page');  }

...but how do you call that? It'd be a little too easy to write something like:

collect_form( interact_server( update_page ) )

and be temporarily confused about why that's the wrong order (one is a call, one is a parameter), try

collect_form( interact_server( update_page() ) )

and maybe end up on

collect_form( () => interact_server( () => update_page() ) )

but, whenever you skim past such code, not be entirely sure if that's correct and/or necessary.

Never mind that with more levels, that becomes a lot messier, called things like callback hell[1], with a pyramid of closing brackets.


Also, none of this makes it easier to pass data around, so it's tempting to stick that in a global.

Also, error handling usually just consists of "if it breaks, it breaks". It's hard even just to have a single overall error path guaranteed to be called when any part borks. Or be a bunch of code spread around, further entangling your code. And none of this makes it easier to debug once it breaks.


All of the above are workable, but none of it's very clean or necessarily maintainable.

In neither case can you really follow the idea of "write code so that the intent is clear".


So if you need to write this sort of code that reacts to other bits, you'll probably start building something that helps you in all this, like some sort of job handler (probably event loop style itself).

Promises

This article/section is a stub — probably a pile of half-sorted notes, is not well-checked so may have incorrect bits. (Feel free to ignore, or tell me)

Promises are intregrated into ES syntax since ES6, and have wide browser support since 2015ish (except IE) [2]


More mechanically, a Promise is a placeholder object that promises that a value will be computed some time later (other language call the same thing a Deferred, or such)

That object represents the bookkeeping state of work that may not have finished yet, plus what should happen once it does finish (or fail).


More practically, a Promise happens to be practical in an asynchronous execution framework, in part because it helps express what should happen when, in part because the execution of such is largely handled for you.


The way Promise syntax works

  • is nice syntactic sugar (a lot less typing than doing the same yourself),
  • is more composable (lets you chain them, and lets you separating responsibilities better),
  • has clearer semantics around errors (and some other details),
  • can make it clearer what code's intent is,
  • can be more readable in general,
  • and makes it easier for error handling to be more thorough and/or readable.


Promises work out as a style to work in.

Promises are a tool, and forcing everything into promises for no reason is usually messier.

And there are many cases in which it's just different, and relatively few cases of where they are categorically better.

That said, there are plenty of cases between "somewhat cleaner" and "will stay cleaner as code changes".

Also, a more APIs (browser or your own) become asynchronous, Promises become a more natural choice to interact with them, and to write code in general.


💤 side notes

Promises are arguably not very clearly named. It's _not_ lazy execution, and you'ld be forgiven for thinking so, particularly if you come from a language where a promise is specifically the name for doing so

While on paper a promise is about the value it will produce, in practice it may be used for the way of expressing work - or even out of necessity (e.g. APIs that are Promise-only, like fetch). (and any state work may effectively be side effects).

If you care to ask about lower levels, it's sort of an event loop system, exposed to you callback-style, except that the syntax takes care of more than that. Yet not having to think about such details is part of the point, and what makes them nice.


Showing the difference practically

To continue the callback style section above, say you have:

function collect_form()    { console.log('collect_form'); }
function interact_server() { console.log('interact_server');   }
function update_page()     { console.log('update_page');   }


Remember our "we can do it but it's a little confusing" take on tying those together?

Promises let you write that something like:

new Promise( function (resolve_func, reject_func) { resolve_func() } ) 
 .then(    function()     { collect_form(); } )
 .then(    function()     { interact_server();  } )
 .then(    function()     { update_page(); } )
 .catch(   function(error){ console.log("error doing this work", error); })
 .finally( function()     { console.log("we finished"); } ); // does nothing, other than demonstrate that it exists

(...which can be shorter if you use arrow functions)


....okaaaay, what did that first line even mean?

It constructs a do-nothing, resolve-immediately promise (with undefined as its stored value, which we don't use).

Why? To get that Promise's
.then()
to actually start a chain of promises.



More gritty details on what Promises contain

A Promise

  • contains a value that will be produced at some point - so that you can later read it out and use it.
some introductions place this entirely central, because the promise class itself does, and...
while this can be a really handy way of handing data forward in that chain
sometimes you use promises just for the execution, and you don't care about that value at all
  • handles the code that produces that value
  • contains calls to the consuming code (onFullfillment, onRejection; more on this below)
  • keeps track of where in the process it is
a
state
that starts as
pending
, and becomes either
fulfilled
or
rejected
. You usually don't need to inspect this state.

There are more moving parts to this, but in a lot of uses you can approach that as "you call the resolve function to set the value and say you're done, or call reject meaning error" (a lot of promises above happen to ignore the second parameter completely, because they don't do error handling).



How do you pass data around?

In real life, you have data.

The above example actually alludes to passing form data to the server, and using data from the server on screen. (We just kept it out for brevity above)


When introducing Promises with data, the Fetch API is a common example, because it's built on a Promise. Consider:

url = 'https://helpful.knobs-dials.com';
fetch( url )
 .then( function (result) { return result.text() } )
 .then( function (text)   { console.log(text)    } );

Or, if you like arrow functions:

fetch( url ).then( result => result.text() ).then( text => console.log(text) );


It's not the most basic example, actually. The way that Fetch was designed it requires having both those two then()s - you can't do

fetch( url ).then( function (result) { console.log( result.text() ) } )

If you do, you'll find out why: fetch's Response.text() call returns not text, but a Promise - which was a design choice (the same goes for .json(), .blob()).

So you have to .then() that first promise, so that it resolves to hand over the text into the function you give (as a value in that second .then's onFinalize function).


Did that last sentence read as somewhat vaguely abstract, and and are promises sometimes an over-designed mess?

Yes.

Are they useful when applied well?

Also yes.



As an exercise, you could try to wrap XHR in Promises (there's not much actual point, because it's asyncronous by itself).

The first, (too-)minimal iteration might look something like:

function XHR_Promise(url) {
  return new Promise(function(resolve, reject) {
    let req = new XMLHttpRequest();
    req.open('GET', url);
    req.onload = function() {
      if (req.status == 200) {
        resolve( req.response );
      } else {
        reject( "request failed" );
      }
    };
    req.send();
  });
}
 
XHR_Promise('/').then(
  function(value) { console.log('yay',value);},
  function(error) { console.log('bah',error);}
);



async and await

sleep as a promise

Function expressions and IIFEs

Text escaping and encoding

(See also Escaping#Javascript)

URL escaping, UTF8 encoding

escape() does the %20%3D thing, but only for ASCII characters.

Unicode is encoded in a javascript-specific way (e.g. U+2222 encodes as %u2222) -- which is non-standard and most servers will not understand, so not recommended


In javascript 1.5 and later, you have two functions that encode as UTF8 before escaping, which is what most servers with unicode support may expect (though there's some leftovers that like cp1252):


  • encodeURI() [3]
converts any unicode to UTF8
escapes everything except ":", "/", ";", and "?"
so that the result is still formatted/usable as a URL.
  • encodeURIComponent() [4]
converts any unicode to UTF8
which %-escapes everything.
Use this when shoving URLs into URL query parameters.

Note that various code has special treatment for URLs in URLs (particularly if they are lone parameters), so you should always check the specs. For example, look at the differences in syndication 'Add to' links.


UTF8 encoding without URL escaping does not exist in Javascript, but consists of simple operations. You could implement your own, or use someone else's, such as this UTF8 encoder and decoder.

other escaping

This article/section is a stub — probably a pile of half-sorted notes, is not well-checked so may have incorrect bits. (Feel free to ignore, or tell me)

HTML escaping (that is to say, & into &amp; and < into &lt; and > into &gt;) can be done in a few ways. One hack you see now and then is to leverage browser innards by using innerHTML and letting it sanitize the string, then read out the same thing, which is probably faster than a few replaces in a row, but also counts on browser behaviour.


A very basic implementation (which may well be more portable) would be:

function escape_nodetext(s) {
   return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}

In attributes, you would also want to replace ", and possibly '.

function escape_attr(s) {
   return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
           .replace(/"/g, '&#34;').replace(/'/g, '&#39;');
}


See also

Interesting details and a few gotchas

Easy regexp/substring test

//given the following string:
var s = 'foo';
 
/o/.test(s) 
   // ...is easier to type than...
s.search('o')!=-1


for and objects

for
will iterate over object members, even if the object is an array, so while you can write:
for (var e in arr) {

this will do something you rarely want, and you probably want:

for (var i=0; i<arr.length; i++) {

(which also means you can't write brief code with that array anonymous, you have to assign it)


This pops up in variations For example, element.childNodes is actually an associative array that just happens to have nice index-based keys, so:

for (i in element.childNodes) alert(element.childNodes[i]);

...prints nodes and two non-element members: item and length. Instead, you should use:

for (i=0;i<element.childNodes.length;i++) alert(element.childNodes[i]);

String replace (by default) replaces only first case

A string-string replace will only replace the first match:

'aa'.replace('a','b') == 'ba'

You can make that global with a syntax-sugared regexp:

'aa'.replace(/a/g,'b') == 'bb'

When the thing to be replaced comes from a variable you'll have to compile it into a regexp more explicitly:

var pattern='a'; 
'aa'.replace(new RegExp(pattern,'g'), 'b') == 'bb'


Page reload/navigate

This article/section is a stub — probably a pile of half-sorted notes, is not well-checked so may have incorrect bits. (Feel free to ignore, or tell me)

I never know which to use. Are any of these incompatible?(verify)

It seems window is effectively interchangeable with document -- except of course in the presence of frames, since they may refer to different things.


window/document.location.reload()

  • if document has POST information, browser will resubmit, and ask the user about that


history.go(0)


document.location.href=window.location.href

  • You can also assign to .location (and not its .href child), but that seems to be a bit of a compatibility hack that you cannot assume will always be there.


document.location.href=window.location.replace(url)

  • Similar to the last, but replaces the current history item (which is primarily handy when reloading this way)


document.location.reload()

  • There is an optional parameter: true instructs the browser it should reload form the web server (which it may not necessarily do(verify)), and false means it should use the cache.


sort() notes

(webdev search for javascript+sort)

JS sorting is a little peculiar.


For starters, basic sort is lexical:

sa=["11", "2", "1"]
sa.sort()
...yields ["1","11","2"]
 // also:
na=[11,2,1];
na.sort()
...yields [1,11,2]

Yes, lexical even for numbers. This presumably means it is using the string representation - possibly a decorate-sort-undecorate thing?(verify)


Other sorters can be made by using comparison functions.

For example, a numerical sorter can be made by with something like:

function naturalSort(a,b) { return (a-b);}

This is also a natural sorter of strings-in-numbers, because arithmetic coerces numbered strings (e.g. "2"-"11"==-9).

na.sort(naturalSort)
...yields [1,2,11]
 
sa.sort(naturalSort)
...yields ["1","2","11"]


You can apply whatever logic you want in the sort functions, sorting values arbitrarily, using external data, and whatnot. Example:

ta=[["Smith",2],["John",11],["Quu",0],["Norg",6]]
 
ta.sort(function(a,b){return a[1]-b[1]})
...yields [["Quu",0],["Smith",2],["Norg",6],["John",11]]


Reverse sorting can be done by inverting the logic:

function revNaturalSort(a,b) {return (b-a);}
function revLexSort(a,b)     {return (b<a);}

...or by using .reverse() on a list after sorting it.



helper stuff; some useful object functions / utility classes / class methods

String

I couldn't find a whitespace strip()per, so I made my own. Pretty basic, could possibly be faster with an index-based implementation.

String.prototype.strip=function(){return this.replace(/^[\s]+/,'').replace(/[\s]+$/,'');};
 
//and a test
alert('['+ '  a b  c  '.strip() +']');


Also because I needed it, a left-padder: (could use some cleaning)

//left-pads a string up to some length.  (using a space. Uses value of 'ch' instead, if given. 
//Make this a string of length 1)
String.prototype.lpad=function(to,ch) {
  var c=' '; if (ch) c=ch;
  var n=Math.max(0,to-(''+this).length);
  var ret='';
  for (var i=0;i<n;i++) ret+=c;
  ret+=this;
  return ret;
}

Calling 'foo'.lpad(5) would give "  foo", calling 'bar'.lpad(6,'-') would yield "---bar".


int / float parsing

Regular expressions

Random things

This article/section is a stub — probably a pile of half-sorted notes, is not well-checked so may have incorrect bits. (Feel free to ignore, or tell me)

By default you only have Math.random(), which returns 0.0 ≤ value < 1.0

In all cases, note the ≤ and < difference.


To get a random integer 0≤value<n, you can do something like:

function randomInt(n) {
  return Math.floor(n*Math.random());
}

Or m≤value<n:

function randomInt(m,n) {
  return Math.floor((n-m+1)*Math.random())+ m;
}


You may also care for things like:

function random_choice(fromlist) {
  return fromlist[Math.floor(Math.random() * fromlist.length)];
}

And (TODO: check for edge cases)

function random_weighed_choice(d) {
  /* given a string:weight mapping, return a weighed random string */
  let total=0, upto=0;
  for (const key in d) { total+=d[key]; }
  let r = Math.random()*total;
  for (const key in d) {
    let w = d[key];
    if (upto + w > r)
      return key;
    upto += w;
  }
}


Array member tests

There has been an includes() with wide support since ~2017 [5]

['1',2,6].includes( 1 ) == false
['1',2,6].includes( 2 ) == true


Before that, you would need checking via manual iteration. One example is below.

Note that as per javascript's equivalence tests ('1'==1 being true and such), the below may be fuzzier than you want it to be. You can require the type to be the same using the second parameter.

Given:

function in_test(ary,stricttype) {
  for (var i=0; i<ary.length; i++) {
    if (ary[i]==this) 
      if (stricttype) return typeof(ary[i])==typeof(this)
      else return true;
  }
  return false;
}
String.prototype.in = in_test;
Number.prototype.in = in_test;
//you could add it on Boolean, Date, but would you use it?
 
//  and/or
 
Array.prototype.contains = function(o,stricttype) {
  for (var i=0; i<this.length; i++) {
      if (this[i]==o)
        if (stricttype) return typeof(this[i])==typeof(o)
        else return true;
    if (this[i]==o) return true;
  }
  return false;
}

You then have

a=['1',2,6];
 
a.contains(2); //true
 
'1'.in(a);
(1).in(a,true); //type not identical, so false
(1).in(a);      //no type check, so true

Set-of-string operations

Since ES6 there is the more flexible Set [6] [7]


Before then, you can e.g. exploit object properties being hash-based to make fairly efficient set operations.

However, you should assume this will only work on string values (or rather, when all values don't lose meaning when you toString() them, because that's effectively what will happen).

//Unique-ify list. (set-in-list)
function unique(l) {
  var o=new Object(),ret=[];
  for(i in l){o[l[i]]=1}
  for(k in o){ret.push(k)}
  return ret;
}
 
function union(l1,l2) {
  var o=new Object(),ret=[];
  for(i in l1){o[l1[i]]=1}
  for(i in l2){o[l2[i]]=1}
  for(k in o){ret.push(k)}
  return ret;
}
 
function intersect(l1,l2) {
  var o=new Object(), ret=[];
  for (i in l1) o[l1[i]]=1; //the value is ignored 
  for (i in l2)
    if (o[l2[i]]!==undefined)
       ret.push(l2[i]);
  return ret;
}
 
function except(l1,l2) {
  var o=new Object(), ret=[];
  for (i in l1) o[l1[i]]=1;
  for (i in l2) delete(o[l2[i]]);
  for (i in o) ret.push(i);
  return ret;
}

Timers

To delay execution of code:

timerID = setTimeout(func, msdelay) [8]
clearTimeout(timerID) [9]

To trigger regularly until you clear the particular ID:

timerID = setInterval(func, msdelay) [10]
clearInterval(timerID) [11] 

In both cases, remembering the ID lets you cancel what you planned (though for a single timeout you often don't care to).


Event execution details

Using these effectively means moving the code out of the current code block, and into a future event.

JS is single-threaded, so any block of code delays others. Events in general (including timers, user input, and XmlHttpRequests) are added to a set. Whenever it's not busy working on another code block (in-page code or event handlers), JS chooses one (presumably with preference(verify)) and executes it.


There are also some interesting details when it comes to interaction with the browser - in part because the browser is multi-threaded.


Gets used for UI loops, and for magic spice that makes something display faster.

These days there are more elegant ways.


General notes

  • note that the delay on a timer is effectively a minimum delay.
  • The blocking, one-at-a-time nature of event execution implies that you can't expect timeouts and delays to be very exact, particularly if any code black can take a while.
  • Another reason is that the backing timer isn't guaranteed to be much higher resolution than perhaps 10ms (or Windows's 15.6ms).
  • ...which also sets a minimum delay you can expect.
  • Browsers may clamp these values to a minimum, such as 10, 4, 2, or 1 (often related to the minimum they can usefully handle). This means your setTimeout(f,1) may actually mean setTimeout(f,10) (unless 0 is a special case?(verify))
  • Interval timers
    • are not guaranteed to be particularly regular, nor guaranteed to fire when things are busy. The interval refers to when events are added, and each timer will only have one of its own event queued (presumably to avoid a number of problems). If the interval's even is still queued when a new one would be (because the code takes long, or was postponed long enough because of other code), a new event will not be queued.
    • may have a minimum - that may be larger than the setTimeout minimum, and may depend on other things (e.g. FF inactive tags)
  • lower-delay timers can be imitated via HTML5's postMessage[12] (example)
  • You can pass in string code or a function reference.
    • the string method just means an additional eval() (apparently at global scope), and can sometimes bothersome with quotes and escaping
    • with the function reference method, firefox allows you to pass in parameters. Other browsers (at least IE) does not support this. To do this anyway you can use an anonymous function that does the actual call you want (or some variation of that)
    • it seems to be a fairly common mistake to keep parentheses on the function parameter. In the string style it gets evaled and therefore executed immediately (in the regular style it would get executed before even being passed in, but people rarely do that).
  • Within event code, 'this' refers to the window object

On timers and onload

On being single-threaded

This article/section is a stub — probably a pile of half-sorted notes, is not well-checked so may have incorrect bits. (Feel free to ignore, or tell me)


JsS is fundamentally single-threaded - one piece of code runs at a time.

Any block of code delays others. Events in general (including timers, user input, and XmlHttpRequests) are added to a set and JS chooses one to run whenever it's not busy working on one.


This is potentially quite neat - it's harder to create race conditions and you don't often need locks, and if everything you do fits a cooperative multitasking even model, you avoid a lot of scheduling. Node.js is in part an experiment to see how far you can take that on the server side too.


Take-home: don't make humongous functions, your won event-loop handlers, etc.


Each window/tab gets its own context (often isolated - with some exceptions, e.g. windows created by another(verify)), and each behaves as if it is a single thread.


localStorage seems to violate things a bit. That is, multiple contexts can access it at the same time. This could be a nice means of IPC -- except its API seems asynchronous so it will race.



...and browser interaction

Javascript, the DOM, and page rendering are separate communicating things (in a way that is not quite standardized, so behaviour can vary).

Assume that DOM updates and page rendering catches up only after each chunk of code.


This means that for responsiveness, it makes sense to do use setTimeout to queue code (is a bit more cooperative multitasting style). For example, when you want a "Loading..." message, you may find it gets shown later than you think, until you do something like:

function ajaxy_stuff() {
  loading_message.show(); //done now
  setTimeout(function(){
     blocking_fetch();
     loading_message.hide();
  },1); //placed into event queue; possibly (but not necessarily) done almost immediately after
}

Note that in various browser implementations, setTimeout with a delay of 0 means "run immediately, and cause the browser's JS thread to yield"(verify) (as in cooperative multitasking), which lets the rendering thread catch up, quite possibly shows the message, and then continue with JS.

If you didn't chances are it would show and hide the message only when the fetch is done.

Number formatting

This article/section is a stub — probably a pile of half-sorted notes, is not well-checked so may have incorrect bits. (Feel free to ignore, or tell me)

You'd think there would be something like C's sprintf, C#'s string.Format, or even Java's number formatting.

You'd be wrong.


There are various libraries (e.g. [13], [14], and more) that people have created.


Some helpful functions were introduced relatively recently:


toFixed, toPrecision, toExponential and toLocaleString (wide support since 2009ish?[15])

Number.toFixed(x) avoids scientific notation

(1.2).toFixed(2) == '1.20'
(1.2).toFixed(0) == '1'
(1201).toFixed(2) == '1201.00'
(1201).toFixed(0) == '1201'

Number.toPrecision(x)

(1.2).toPrecision(1) == '1'
(1.2).toPrecision(5) == '1.2000'
(1201).toPrecision(1) == '1e+3'
(1201).toPrecision(5) == '1201.0'

Number.toExponential(x) forces scientific/exponential notation even when it is not necessary:

(1.2).toExponential(1) == '1.2e+0'
(1.2).toExponential(5) == '1.20000e+0'
(1201).toExponential(1) == '1.2e+3'

toLocaleString() uses locale-specific thousand/digit separators: (use for final presentation only, since you can't assume they'll parse back)

(1201).toLocaleString() == '1.201'   // (...for me, that is)


There is a relatively established hack via Math.round():

Note that the result of the below is still a number, and that half the trick is to play JS's own limited-precision-showing, trailing-zero-chopping behaviour.

Math.round(1.234*10)/10       == 1.2
Math.round(1.234*100)/100     == 1.23
Math.round(1.234*1000)/1000   == 1.234 
Math.round(1.234*10000)/10000 == 1.234 



date and time

A little hairy. In general, when handling dates, 'consider using libraries (e.g. Moment.js, Date.js)

What I need most of the time is one of:

  • Date.now(): milliseconds since 1970 epoch.
  • new Date() without arguments constructs a Date for the current time
  • new Date() with a specific date as argument is one of:
    • new Date(number) - parsed as milliseconds since 1970 epoch, e.g. as fetched from Date.now()
    • new Date(year, month[, date[, hours[, minutes[, seconds[, milliseconds]]]]]) - constructed as such
    • new Date(dateString) - handed to Date.parse, which understands:
      • a basic subset of the ISO 8601 formats [16] (mostly fixed to Zulu?)
      • Conventionally also RFC 2822 style [17] but that's not standard.

Notes:

  • A bunch of specific-format outputs (including ISO8601) but no strftime style formatter function (), you have to roll your own or find a library
is made more interesting because of the next two points.
  • month is 0-based
  • defaults are 0 for various things (verify)


See also: