Javascript notes - syntax and behaviour

From Helpful
Jump to: navigation, search
Related to web development, hosting, and such: (See also the webdev category)
jQuery: Introduction, some basics, examples · plugin notes · unsorted

Server stuff:

Dynamic server stuff:

Optional function parameters

Javascript fills in undefined values for any ommitted parameters, so all are optional -- it's just that a lot of code will assume things are there and trip over their absence. So it's optional-by-loose-contract.

You could write something along the lines of:

function upper(s,alsoStrip) {
  var r=s.toUpperCase();
  if (alsoStrip!==undefined) {r=r.strip();}
  return r;
}

In this particular case, undefined, null, false and 0 are all coerced to false. In other cases you may specifically want to have 0 be a value you can pass in, in which case you would probably check against undefined as the 'unspecified' value.


this

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

For example:

  • In object members, they are the object
  • When used as event handlers, they are the DOM element they are set on
  • in nonmethod functions, it 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

Classes

You can take a basic function definition, such as:

function sorta_class() {}

and do

thing = new sorta_class()

The use of new means creating a new Object (remember that most everything is objects).


This acts like a instantiated objects from a class in that:

  • you use them that way
in that you can do the new thing repeatedly, and set data on each individually
  • you can use this in that constructor
  • you can add prototypes to that sorta-class (lets you use this)
  • prototype inheritance stuff works

Half of that demonstrated:

function sorta_class(i) { this.i=i }
sorta_class.prototype.alerti = function() { alert(this.i); }


Forgetting new was a mistake - it doesn't fail but behaves differently (e.g. this doesn't refer to the object). So since ES6 it's cleaner to use:

class sorta_class {
  constructor(i) {
    this.i = i;
  }
 
  alerti() {
    alert(this.i);
  }
}


after either of those you can do:

thing = new sorta_class(5);
thing.alerti();


The difference beyond that syntactic sugar:

  • ES6 gives you super()
  • ES6 requires use of new on classes
before it you could also directly invoke the thing. Often didn't make much sense, but you could.
  • function definitions are hoisted, meaning they act as if cut-and-pasted to the top and evaluated first, meaning order of declaration no longer matters
whereas classes only exist after execution has reached and passed their definition


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


Objects as (hash)maps

Any object (including DOM) acts like like a hash map.

That is, you can set and get any attribute and use it for data of your own. Sometimes makes for nice hacks.

Arguably you should only do this on {}, and new Object() informed of its members, because you may randomly overwrite member functions, affect the DOM, and this can sometimes cause confusion.

A caveat is that keys are always strings. If you try to store anything else in the key, it effectively will be toString()'d(verify). If you use only numbers, only strings, or such you'll be fine, but this can be nasty when you try to use objects as keys.

Create a new object:

var o=new Object();
o['b']=5; //arbitrary strings
o.a='3';  //this style access the same, but requires syntactical validity
o[1]=2;   //implies "1"
delete(o[1]);

for (var i in o)  alert(''+i+': '+o[i])

You can test for presence/absence by doing an identity comparison against undefined, e.g.:

var val=o[4];
if (val===undefined) alert('not there')

...except that you can also store undefined as a value; I'm not sure how to distingish, though in many cases you won't need to store undefined, especially when you also have null, and null!==undefined But! - null==undefined, so it is probably worth it to implement a few simple wrapper functions around this to avoid accidentally using == instead of ===.

Note: There is also a hasOwnProperty(s)


There is a minimal STL-like library (no algorithms, mostly just the basic collections), see [1].


for and objects

for
will iterate over object members (even if the object is an array).

This means any additional members (such as prototype functions added by libraries) will mess up iteration written with a foreach in mind, such as:

for (var e in arr) {

Instead, you want

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


This pops up in different forms. 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, fix, 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 converts 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, fix, or tell me)

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


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

In all cases, note the ≤ and < difference.


Timers

To delay execution of code:

timerID = setTimeout(func_or_codeAsString, mstime) [2]
clearTimeout(timerID) [3]

To trigger regularly until you clear the particular ID:

timerID = setInterval(func,delay) [4]
clearInterval(timerID) [5] 

In both cases, remembering the ID lets you cancel what you planned.


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 also 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[6] (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 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, fix, or tell me)


It's single-threaded - one piece of code runs at a time.


This is potentially quite neat - if everything you do fits that model, you avoid a lot of scheduling. Node.js is in part an experiment to see whether that works.


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.

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


One thread also means you can't race, so you don't need locking, and there is none.

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 wonderful 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 the DOM is updated and page rendering catches up only after each chunk of code.

This means that for responsiveness, it makes sense to use setTimeout to queue code. 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.


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

Of course, there are various JS libraries (e.g. [7], [8], and more) that people have created out of frustration. Various JS libraries also have helper functions (e.g. YAHOO.util.Number.format())


Some helpful functions were introduced relatively recently:

Number.toFixed(x) is probably most useful for currency (JS ≥1.5)

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

Note that IE≤7's toFixed has a bug when dealing with values in in (-0.94, -0.5] or [0.5, 0.94) YUI comment.


Number.toPrecision(x) (JS ≥1.5)

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

Number.toExponential(x) (JS ≥1.5?) 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() (JS ≥1.0) uses locale-speicif thousand/digit separators: (use for final presentation only, since they may not parse back)

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



The relatively established hack (and to be compatible with JS<1.5 and avoid the IE bug) is to use using 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 ISO8601 formats [9] (mostly fixed to Zulu?)
      • Conventionally also RFC 2822 style [10] but that's not standard.

Notes:

  • no formatter function, you have to roll your own
is made more interesting because of the next two points.
  • month is 0-based
  • defaults are 0 for various things (verify)

See also:


Data structure stuff

Array ≈ stack ≈ queue

Given that:

  • push() is 'add to end'
  • pop() is 'take from end'
  • shift() is 'take from start'
  • unshift() is 'add to start'

...you can use an array as a stack using push() and pop()

...and as a queue using push() and shift() (or pop() and unshift())


They're probably not the fastest possible implementations of said data structures, but they can be convenient.


Set-of-string operations

One general programming trick is using the fact that hash keys are per definition unique to create set operations, like the following.

However, since hash keys are always strings in JS, this isn't type-robust - this only works when all values are strings, or rather, when all values don't lose meaning when you toString() them, because that's effectively what it will do.

//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;
}


Array member tests

No cleverness, just a simple and stupid iteration thing.

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

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

Code:

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?
 
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;
}

Typing is exciting

Object types

typeof(something):

  • Things you tend to use as if they are simple values report as themselves:
    • "number" "string" "boolean" "function", and "undefined"
  • Things like Array, Date, Math, RegExp and Error and null are considered:
    • "object"

You can use this e.g. to make libraries more flexible, by alowing them to take both a DOM element reference and a document ID.


Because Specs

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, fix, or tell me)
There are no integers, only
Number
, which are floating point.

But (floating point can by nature store integers exactly within a range, and because they're 64-bit floats, that works up to here up to 253-1 which suits plenty of uses.

Outside that range, though, you'll get bitten by the thought integers even exist:

9999999999999999 === 10000000000000000


There are some integer operations, like
~
for bitwise NOT, calculated on two's-complement integers - but the implied conversions around it make for some rough (though understandable) edges, e.g.
~1E30 == -1


BigInt has existed for a while, with syntax like
2n ** 160n
. But there is no elegant way to duct tape it to the type system, so has some rough edges, particularly that mixing them with Numbers doesn't work, e.g.
1n+1
, is a TypeError. BigInts certainly have their uses, but you may want to avoid them unless you really need them.)


Enthusiastic coercion means never having to say TypeError:

"str" + 1 is "str1"   
"str" - 1 is NaN      
"0"+1     is "01"     lesson: + is overloaded in ways that make it coerce to string more easily
"0"-1     is -1       where other operators would more easily convert (which can more easily fail)

+[1]      is 1        because unary + is ToNumber, in this case ToNumber(toString(input)) [11]
+[1,2]    is NaN      because of the above, and ToNumber can't deal with "1,2"

! + []    is true     because that's  unary !,  on unary + on [],
                      and +[] reduces to ToNumber(""), which is 0,
                      and !0 is implicitly coerced to Boolean

! + [1,2] is true     because that's  unary !,  on unary + on [],    (i.e. !Nan)

3 - true ==  2       while true!==1 (essentially because Boolean is own type)
                     true in Number context becomes 1. Which if you're going to allow it is fair.


typeof(null)     == "object"          
typeof(null)     != typeof(Object)    ...because:
typeof(Object)   == "function"        (...because it's uninstantiated)

NaN !== NaN        which is what floating point semantics say it is, 
                    as not all reasons for NaN are the same.
NaN != NaN         ...can be considered slightly weirder, given how fuzzy and coercive == often is


[] + []    is  ""
[] + {}    is  "[object Object]"
{} + []    is  0                 ...or "[object Object]"
[] + {} === {} + []
{} + {}    is  NaN               ...or "[object Object][object Object]"


Context matters. Some of the last bunch are cheating, really, because most of what's happening is that {} in some cases is an empty code block (when in the middle of nowhere), in other cases an empty Object (e.g. when assigned to a var), which in most code you just wouldn't not confuse accidentally.

Most places you're trying this (e.g. the console) will be the former. Note that you can force context to be an expression with var/let/const.


So e.g.
{} + []
is actually a loose
+[]
, and unary + coerces things to numbers, which turns out to mean
ToNumber("")
which is 0.

In expression context {} is an empty object so it reduces to "[object Object]" + ""


{} + {
} is
+{
} which is
ToNumber("[object Object]")
which is NaN

In expression context it's two empty objects coerced to strings, so "[object Object][object Object]"


Array.toString is interesting / undefined is interesting:

Array(3) == ',,'    
Array(3)[0] === undefined     yet
Array(3)[0].toString()        is a typeError because undefined isn't an object
{undefined:3}                 

...so apparently undefined is allowed in some contexts that I expect to toString.


{} is object

And because of that, {} isn't really a hashmap, it's setting members on an object.

Which happens to be backed cia a hashmap, but with Extra Details: in particular it implies that setting keys via
[]
is always coerced to strings, which means that some things are "[object Object]", or rounded or whatnot, and fail spectacularly.

And you can't change the way it gets converted to string unless you do it all manually.


See also:

undefined, null and object detection in general

Note that:

  • undefined isn't null
or rather
undefined===null is false
undefined==null is true
  • null is a primitive value null is not an object
yet typeof null == 'object' because the specs say so (you can consider this a misfeature due to historical reasons)
  • undefined as a value means 'was declared by never assigned' (except you can assign undefined)
functions that don't expicitly return undefined
uninitialized function arguments are undefined
technically, it is a primitive value with its own type
  • javascript itself basically never sets null(verify). This is a value that you use, usually in a sense of "I did get around to setting this, there is just no sensible value for me to have set", or in a "...yet" sense.
  • undefined can also be used to check "is this unassigned or undeclared" -- mostly because typeof does not require its argument to exist, and typeof undefined is 'undefined' (it's its own type)
so a solid test is
if (typeof x === 'undefined')
...and
if (x === undefined)
is not due to the possible ReferenceError
Note that this is not really necessary when asking for Object members (e.g. including window.something), because those will be undefined rather than referencerrors


There is an argument that you don't need both. Yes, we've invented meanings for the difference, but none of them are necessary.


  • you should define your variables, and initialize them if sensible. You can assign things you have not defined as local (though var), but fetching things from non-defined variables will bork:
var a;
var b=3;
var c=null;
alert(a); //will show 'undefined' 
alert(b); //will show '3' 
alert(x); //will stop execution with something like Uncaught ReferenceError: x is not defined
alert(c); //would display 'null' (except JS just borked)</nowiki>


~

Bitwise NOT

Unless you understand two's complement, you may have no use for this -- other than
~~x
is effectively a shorthand for
Math.floor()
Note there are other things that effectively do floor(), like
x|0
,
x||0
, and
x>>0

(and remember does not have an integer type at all)


(all more or less the same speed, at least these days)

== versus ===

See also:

Unsorted

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, fix, or tell me)


A particular model of deferring code. (arguably not a very well named one)


At heart it's still largely callbacks, but with a compared to basic callback code, promises are a good deal of syntactic sugar (e.g. avoiding a bunch of boilerplate), but also add some nice semantics that make things better defined, more composable, and makes error handling a little easier.


As to why this can make some things easier and cleaner (mostly things that need to happen in sequence and/or take some time), an example.

Say you want to show something in your interface, and your code comes down to:

some_interaction(); 
work_stuff();   // e.g. some fetching of data and processing what to show
final_work();   // e.g. displaying the result


Doing that synchronously (as written there) in a single-threaded model will block the UI thread until it's done completely.


It often goes a long way to have each part call the next one like
setTimeout(nextfunc,0)
.

While this is effectively co-operative multitasking (yay), it has its drawbacks:

It's verbose
functions themselves need to call the next
so either tangled hardcoded, or hand in the function to call
which makes the code's intent less than obvious,
and does not make error handling easy because, parts are unaware of others
spreading error paths around, making it harder to do the right thing even if you want to


The promise syntax lets you write much the same:

some_interaction().then(function(result){
    return other_interaction(result);
}).then(function(result2){
     final_work(result2);
}).catch(function(error) {
     // e.g. say "error doing whatever this thing represented"
});

Because this is a more declarative wrapper,

it's more terse,
makes intent clearer (and sometimes the relation between parts)
and errors in any part end up in the same place.

(even when you can do nothing useful in that error handler, you can still more easily report what overall action failed)

Note that since promises are objects, you can still rip them out and abuse them callback style and get all the downsides of those. Up to you.


In some cases it makes sense to aggregate a list of parts. If the first two calls in the above example happen to be unrelated (and only then, because it's fail-fast), you can also do:

Promise.all(
 [some_interaction(), other_interaction()]
).then(function(results) {
    final_work(results); // note that result is an array of the individual results
}).catch(function(error) {
});




At lower levels

A Promise itself

  • represents a value that will be filled in at some point in the future
  • has events that can be triggered when they become known
  • is made to be chainable


At a technical level, a Promise

  • is an object that
    • may produce a single value in the future
    • is in state pending, or settled into fulfilled or rejected.
    • once settled, it must have its value be settled
    • may have callbacks onFulfilled and onRejected

Often we think of it as value or error, with value representing success.


Implementationwise, something that wants to be interactive this way returns a Promise object, and will be doing the work to finish it in the background.


https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-promise-27fc71e77261 https://developers.google.com/web/fundamentals/getting-started/primers/promises https://davidwalsh.name/promises

https://caniuse.com/#feat=promises


async

await

scoping details

the default; var; let

From most scopes, variables are global by default (which in browsers means 'a member of window').


var makes variables local to function scope

So generally, using var in all your functions and libraries was always a good habit to avoid clashes.


ES6 added block scope, so added more scoping rules:

  • var
    scopes to the nearest function
  • let
    scopes to the nearest enclosing block


This has fired up a whole let is the new var discussion, in the sense that people should default to using let.


That discussion is mostly two camps:

  • let is the same in some cases, and more controlled in others,
  • let is better for people who come from block-scoped languages

...versus

  • there are few real situations where let is fundamentally better
  • you generally shouldn't make functions where this really matters (particularly monster functions)

Also:

  • arguably, the main difference is that the mental model is now harder.
    • without googling: what happens with let at global scope, and why?
    • why is ES6's block-scoped
      const
      allowed globally when block-scoped let is explicitly not, beyond "because specs"?



const

const makes constants at block scope

Like in many languages, though, const does not mean immutable. In ES6 because the reference (more specifically the binding) is protected, while the object/value being pointed to is not.


So e.g.

// while for primitives that implies what you'ld think:
const c=3; 
c=4;      // is a typeerror

// this:
const o={};
o.foo=5;  // is fine


In other words

  • it can't hurt to define things const when you won't alter them
  • compared to let
but useful to signal intent to coders (and compiiler(verify))
also const is allowed globally (let is not)


  • it's useful to signal code's intent
  • it's useful to protect you against yourself within this scope
  • ...and little more than that


various ways of using functions

Since funtions are themselves objects,

function foo() { /* code */ }

is effectively shorthand for:

var foo = function() {
  /* code */
};


If you want to run code without putting a reference to it in the scope you're at (sometimes useful in libraries), you can use an anonymous self-invoking style:

(function() { 
  /* code */
}(arguments));

Function expressions

Immediately Invoked Function Expression
Arrow function

strict mode/sloppy mode

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

Strict mode changes some semantics, mostly to avoid some common mistakes, and throw exceptions for some things where it previously would silently ignore nonsense. See e.g. [12]


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


modules and importing

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



Text escaping and encoding

(See also Webdev_notes#Escaping)

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) that is non-standard and most servers will not understand.


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 (see also cp1252, though):

encodeURI(), in which the escape step does not %-escape ":", "/", ";", and "?" so that the result is still formatted/usable as a URL.

encodeURIComponent(), 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, fix, or tell me)

HTML escaping (that is to say, & into &amp;, < 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