Hello, my name is Seth Bergman. I am a

Full Stack Engineer

focused on helping companies scale. I love learning about software architecture, containers, open source programming and automation. I use technologies that drive innovation, speed up development and provide continuous delivery of awesome software.

ES6 Learning Notes - Variables and Parameters

The let Keyword

let allows us to define variables in a different way than var.

With var there are two types of scope: global when the variable is defined outside of the function, and function scope for variables declared inside of a function.

JavaScript has no block scope. In this example, it seems like x would only be available inside of the if, but that's not the case:

var doesSomething = function(param) {

  if(param){
    var x = 5;
  }
  return x;
};

x is available throughout the doesSomething function, and the return will work because there is going to be a variable named x.

let gives us true block scoping:

var doesSomething = function(param) {

  if(param){
    let x = 5;
  }
  return x;
};

In the above example, there will be an error if no other x variables are defined in an outer scope and the if block isn't hit.

Here is an example of behavior in a for loop:

for(var i = 0; i < 10; i++){

}
return i; // returns 10

for(let j = 0; j < 10; j++){

}
return j; // ReferenceError: j is not defined  

let will be the replacement for var because it will let us declare variables where we need them, avoid hoisting, and give us true block scoping like most developers expect.

The const Keyword

const creates and initializes a read-only variable at a constant value that you can never change.

In ES6, const has block scoping:

var doSomething = function() {  
  const x = 12;
  var x = 10; //Syntax error: Variable 'x' has already been declared.
}

The x defined in doSomething shadows the outer x:

const x = 12;  
var doSomething = function() {  
  let x = 10; // Note: would also work with `var`
  return x; // 10
}

Note: it is convention to use ALL CAPS for constants, but the above is for illustrative purposes.

Destructuring

Destructuring allows you to assign values to a set of variables.

let x = 2;  
let y = 3;

[x, y] = [y, x]; // x is now 3, and y is now 2

What we see on the right side of the destructuring assignment in the above example is an array built from the values that are in y and x.
However, on the left side, it is not an array, but rather the individual variables x and y surrounded by square brackets ([]) because we are destructuring an array.

In other words, in the above example we are taking the value of the variable in the first index of the array [y, x] (in this case 'y = 3') and assigning it to x, and taking the value of the variable in the second index of the array [y, x] (in this case x = 2) and assigning it to y. Thus, our variables have switched values.

Variable assignment can also be done from a function that returns an array:

var doSomething = function() {  
  return [3, 2];
}

let [x, y] = doSomething(); // x = 3, y = 2  

If there was an additional member of the array, but you still wanted x to be 3 and y to be 2, a , can be added to skip the value the destructuring assignment:

var doSomething = function() {  
  return [1, 3, 2];
}

let [, x, y] = doSomething(); // x = 3, y = 2  
Object Destructuring

In this example, we create variables first, last, and twitter and assign them to the return values of the doSomething function.

let doSomething = function() {  
  return {
    firstName: "Charles",
    lastName: "Bronson",
    twitter: "@NoDice"
  };
}
  let { firstName: first,
        lastName: last,
        twitter: twitter } = doSomething();

It looks like we're creating an object literal, but we're not.

Think of the left hand side as being how you navigate into an object:

let doSomething = function() {  
  return {
    firstName: "Charles",
    lastName: "Bronson",
    handles: {
        twitter: "@NoDice",
        facebook: "bronson69"
      }
    };
}
  let { firstName: first,
        lastName: last,
        handles:{twitter: twitter} } = doSomething();

If you are cool with having your variable names be the same as the properties, you can use the shorthand:

let { firstName, lastName, handles:{twitter} } = doSomething();  
Destructuring in a function

The old way when working with a function:

  let doSomething = function(url, config){
    return config.data; // "test"
  };

  var config = { data: "test", cache: false }

  let result = doSomething("api/test", config);

New way:

  let doSomething = function(url, {data, cache}){
    return data; // `test` comes directly from the `data` property
  };

  let result = doSomething(
        "api/test", {
          data: "test",
          cache: false
        }
      );

Default Parameter Values

In JS, the old way of handling a missing or falsey parameter:

var doSomething = function(name){  
  name = name | "Taylor";
  return name;
};

The new way in ES6 is like so:

var doSomething = function(name="Taylor"){  
  return name;
};

Note: The default parameter syntax has some different behavior with different calling parameters.

let result = doSomething(); // "Taylor"  
let result = doSomething(""); // Undefined  
let result = doSomething(undefined); // "Taylor"  
let result = doSomething("Colleen"); // "Colleen"  
let result = doSomething(null); // null  

Combining Concepts:

let doSomething = function(a = 1, b = 2, c = 3){  
    return [a, b, c];
};

let [a,b,c] = doSomething(5, undefined); // a = 5, b = 2, c = 3  

Example with destructuring:

let doSomething = function(url, {data = "Taylor", cache = true}){  
  return data;
};

let result = doSomething("api/test", { cache: false }); // "Taylor" is returned (and `cache` is `false` inside the function)


# Rest Parameters
Rest parameters are how you deal with an unknown number of params with a function.  

The old way was to use the implicit arguments object that holds are the args passed into a function:

let sum = function() {  
  let result = 0;
  for (let i = 0; i < arguments.length; i++) {
    result += arguments[i];
  }
  return result;
};

let result = sum(1, 2, 3) // 6  

One of the problems though is that arguments looks like an array, but it really isn't (it's an object that has some of the qualities of an array).

The new way in ES6 gives us a better syntax to achieve the same result:

let sum = function(...numbers) {  
  let result = 0;
  for (let i = 0; i < numbers.length; i++){
    result += numbers[i];
  }
  return result;
};

The rest parameter is always the last parameter, and features the ... prefix.

This version of sum() has a parameter named numbers, and incoming values passed in are packaged into a true array for the function to use.

Another example:

let doSomething = function(name, ...numbers){  
  let result = 0;
  numbers.forEach(function(n){
      result += n;
    });
  return result;
};

let result = doSomething("Taylor", 1, 2, 3); // 6  

If you don't pass any numbers in, then the function is fed an empty array, and result is returned as 0 like we specified.

Spread Operator

The spread operator uses the same ... syntax as the rest parameter. However, when used outside a function parameter list, the ... means we want to spread an array across individual parameters.

In this example, 1 goes to x, 2 goes to y, and 3 goes to z:

let doSomething = function(x, y, z) {  
  return x + y + z;
}

var result = doSomething(...[1, 2, 3]); // gives us 6  

Building Arrays with the Spread Operator

var a = [4, 5, 6];  
var b = [1, 2, 3, ...a, 7, 8, 9]; //b is now [1, 2, 3, 4, 5, 6, 7, 8, 9]  

Spreading makes data we already have in arrays easier to work with.

Template Literals

Template literals can help to build string values we may want to assign to a variable, among other uses.

Old school string concatenation becomes tedious:

let category = "music";  
let id = 2112;

let url = "http://apiserver/" + category + "/" + id;  

In ES6, there are some differences:
* The string is delimited with backticks (``) * Inside of the backticks, you can have literal text and substitution placeholders via ${placeholder}

let category = "music";  
let id = 2112;

let url= `http://apiserver/${category}/${id}`;  

A more interesting example:

let upper = function(strings, ...values){  
  let result = "";
  for (var i = 0; i < strings.length; i++){
    result += strings[i];

    if (i < values.length){
      result += values[i];
    }

  }

  return result.toUpperCase();
};

var x = 1;  
var y = 3;  
var result = upper `${x} + ${y} is ${x + y}`; // Gives us "1 + 3 IS 4"  

You can associate a tag with a template. In this case, the tag upper is a function that is invoked.

The first parameter being passed into the upper() function is known as a parsed template string. These are the pieces of literal text in the template, chopped into an array such that the substitutions are removed. The second parameter is a rest parameter (also an array) using the values of the substitutions.

strings = ["", " + ", " is ", ""];  
values = [1, 3, 4]  

This shows us that there are lots of possibilities in terms of tags that take a template and encode it, tags for localization, and more.