Over the last couple of months I’ve made a few enhancements to JSHint, mainly as a way of learning ES6 (I’m most proud of re-implementing variable scope detection) and during that process I’ve come across a few things that surprised me, mostly about ES6 but also in ES3, including the following feature, which I’ve never used and this is where I will start.

BREAK FROM ANY BLOCK

You should be aware that you can break and continue from any loop – it is a fairly standard programming language Construct. You might not be aware that You can label loops and jump out of any particular loop…

outer: for(var i = 0; i < 4; i++) { while(true) { continue outer; }}Copy the code

The same applies to both breaks and continues. You will have definitely seen break used with switch statements…

switch(i) {
   case 1:
       break;
}
Copy the code

Incidentally, this is why Crockford suggests your case should not be indented – the break jumps out of the switch, Not the case, but I prefer the readability of indenting case. You can also label switch statements…

myswitch: switch(i) {
   case 1:
       break myswitch;
}
Copy the code

Another thing you can do is create arbitrary blocks (I know you can do this in C#, I expect other languages too).

{ { console.log("I'm in an abritrary block"); }}Copy the code

So, we can put this together and label and break from arbitrary blocks.

outer: {
  inner: {
      if (true) {
        break outer;
      }
  }
  console.log("I will never be executed");
}
Copy the code

Note that this only applies to break – you can only continue in a loop block. I’ve never seen labels being used in JavaScript and I wondered Why I think it’s because if I need to break two layers of blocks, It’s a good sign that the block might be more readable inside a function and there I will use a single break or an early return to achieve the same thing.

However, if I wanted to write code that had a single return in every function, which is not to my taste, Then I could use block breaking. E.g. Take this multiple return function…

function(a, b, c) {
  if (a) {
     if (b) {
       return true;
     }
     doSomething();
     if (c) {
       return c;
     }
  }
  return b;
}
Copy the code

And use labels…

function(a, b, c) {
  var returnValue = b;
  myBlock: if (a) {
     if (b) {
       returnValue = true;
       break myBlock;
     }
     doSomething();
     if (c) {
       returnValue = c;
     }
  }
  return returnValue;
}
Copy the code

The alternative being more blocks…

function(a, b, c) {
  var returnValue = b;
  if (a) {
     if (b) {
       returnValue = true;
     } else {
       doSomething();
       if (c) {
         returnValue = c;
       }
    }
  }
  return returnValue;
}
Copy the code

I prefer the original, then using elses and then block labels – but maybe that’s because it’s what I’m used to?

DESTRUCTURING AN EXISTING VARIABLE

First off, a quirk I cannot explain. It seems in ES3 you can add parentheses around a simple assignment and It works…

var a;
(a) = 1;
assertTrue(a === 1);
Copy the code

If you can think of why anyone would do this, then please comment!

Destructuring is the process of pulling a variable out of an array or object. Most often you will see the following Examples…

function pullOutInParams({a}, [b]) {
  console.log(a, b);
}
function pullOutInLet(obj, arr) {
  let {a} = obj;
  let [b] = arr;
  console.log(a, b);
}
pullOutInParams({a: "Hello" }, ["World"]);
pullOutInLet({a: "Hello" }, ["World"]);
Copy the code

But you can also do it without a var/let/const. With Arrays you can just write it as you expect…

var a;
[a] = array;
Copy the code

But with objects you must surround the whole assignment in parenthesis…

var a;
({a} = obj);
Copy the code

The reasoning was that there was too much scope for confusion with code blocks, since you can have anonymous code blocks and ASI (automatic semi-colon insertion) will convert identifiers to Expressions that evaluate (and as shown in the example below, can have side effects…)

var a = {
   get b() {
     console.log("Hello!");
   }
};
with(a) {
  {
    b
  }
}
Copy the code

Going back to the original example, where we parenthesised our identifier before assignment – you might think that also applies to destructuring. It Doesn ‘t.

var a, b, c;
(a) = 1;
[b] = [2];
({c} = { c : 3 });
Copy the code

DESTRUCTURING WITH NUMBERS

Another aspect of destructuring you might not realise is that the property names do not have to be unquoted strings. They can be numbers..

`var {1 : a} = { 1: true }; `Copy the code

The Or quoted strings…

`var {"1" : a} = { "1": true }; `Copy the code

Or you might want to pull out a computed name…

var myProp = "1";
var {[myProp] : a} = { [myProp]: true };
Copy the code

Which makes it quite easy to write Code…

var a = "a";
var {[a] : [a]} = { a: [a] };
Copy the code

CLASS DECLARATIONS ARE BLOCK SCOPED

Functions are hoisted to their function scope, meaning you can have a function declaration after its usage…

func();
function func() {
  console.log("Fine");
}
Copy the code

as opposed to function expressions which when assigned to a variable, the variable is hoisted but the assignment doesn’t happen until the function expression is assigned.

func(); // func is declared, but undefined, so this throws E.g. "func is not a function"
var func = function func() {
  console.log("Fine");
};
Copy the code

Classes have been a popular part of ES6 and have been widely touted as syntactic sugar for functions. So you might think that the following will work..

new func(); class func { constructor() { console.log("Fine"); }}Copy the code

But even though this is basically syntactic sugar for our first example, it doesn’t work. It is actually equivalent to..

new func();

let func = function func() {
  console.log("Fine");
}
Copy the code

Which means we are accessing func in the temporal dead zone (TDZ), which is a reference error.

SAME NAME PARAMETERS

I assumed it was not possible to specify parameters with the same name, however, it is!

function func(a, a) {
  console.log(a);
}

func("Hello", "World");
// outputs "World"
Copy the code

Except in strict mode…

function func(a, a) {
  "use strict";
  console.log(a);
}

func("Hello", "World");
// errors in chrome - SyntaxError: Strict mode function may not have duplicate parameter names
Copy the code

TYPEOF IS NOT SAFE

Okay, I stole this observation, but it is worth repeating.

Before ES6, it was well known you could always use typeof to safely find out if something was defined, Whether it was declared or not…

if (typeof Symbol ! == "undefined") { // Symbol is available } // The following throws an exception if Symbol not declared if (Symbol ! == "undefined") { }Copy the code

But now this only works if you have not declared the variable using let or const. This because of the TDZ, which makes it a reference error to access the variable before declaration. Essentially the variable is hoisted to the beginning of the block, But it is a reference error to access it. In JSHint’s scope Manager I have to record Performing of a variable, then if it is declared as a let or const within the current block or parent blocks, it is a reference error. If it is declared by a var statement it is valid but a JSHint warning and if it is not declared it is using a global and possibly a different warning.

if (typeof Symbol ! == "undefined") { // Symbol is available } let Symbol = true; // causes a reference errorCopy the code

NEW ARRAY

I’ve always avoided using the new Array constructor. Part of the reasoning is that its arguments can either be a length Or a list of items…

new Array(1); // [undefined] new Array(1, 2); / / [1, 2]Copy the code

But a colleague was using it recently and came across something I haven’t seen before..

var arr = new Array(10);
for(var i = 0; i < arr.length; i++) {
  arr[i] = i;
}
console.dir(arr);
Copy the code

This produces an array of items from 0 to 9. But then, if This is refactored to use map…

var arr = new Array(10);
arr = arr.map(function(item, index) { return index; });
console.dir(arr);
Copy the code

Then arr is unchanged. It seems that new Array(length) creates an array with that length, but does not set any items, so referring to the length works, but enumerating does not. What about if I set a number ?

var arr = new Array(10);
arr[8] = undefined;
arr = arr.map(function(item, index) { return index; });
console.dir(arr);
Copy the code

Now I get an array where the 8th index is equal to 8, but all the others are set to undefined. Looking at the polyfill for map, it loops over every item (hence why index is correct) but uses an in check to see if the property is set. You also get The same behaviour using array literals…

var arr = [];
arr[9] = undefined;
// or
var arr = [];
arr.length = 10;
Copy the code

OTHER GEMS

Mozilla’s developer blog has a great post on arrow functions, which includes details of using