You'll be seeing let and const throughout this book. These are new variable declaration types. Unlike var, let is block scoped; it does not apply outside of its containing block:
let foo = 'bar';
if(foo == 'bar') {
let foo = 'baz';
console.log(foo); // 1st
}
console.log(foo); // 2nd
// baz
// bar
// If we had used var instead of let:
// baz
// baz
For variables that will never change, use const, for constant. This is helpful for the compiler as well, as it can optimize more easily if a variable is guaranteed never to change. Note that const only works on assignment, where the following is illegal:
const foo = 1;
foo = 2; // Error: assignment to a constant variable
However, if the value is an object, const doesn't protect members:
const foo = { bar: 1 }
console.log(foo.bar) // 1
foo.bar = 2;
console.log(foo.bar) // 2
Another powerful new feature is destructuring, which allows us to easily assign the values of arrays to new variables:
let [executable, absPath, target, ...message] = process.argv;
Destructuring allows you to rapidly map arrays to variable names. Since process.argv is an array, which always contains the path to the Node executable and the path to the executing file as the first two arguments, we can pass a file target to the previous script by executing node script.js /some/file/path, where the third argument is assigned to the target variable.
Maybe we also want to pass a message with something like this:
node script.js /some/file/path This is a really great file!
The problem here is that This is a really great file! is space-separated, so it will be split into the array on each word, which is not what we want:
[... , /some/file/path, This, is, a, really, great, file!]
The rest pattern comes to the rescue here: the final argument ...message collapses all remaining destructured arguments into a single array, which we can simply join(' ') into a single string. This also works for objects:
let obj = {
foo: 'foo!',
bar: 'bar!',
baz: 'baz!'
};
// assign keys to local variables with same names
let {foo, baz} = obj;
// Note that we "skipped" #bar
console.log(foo, baz); // foo! baz!
This pattern is especially useful for processing function arguments. Prior to rest parameters, you might have been grabbing function arguments in this way:
function (a, b) {
// Grab any arguments after a & b and convert to proper Array
let args = Array.prototype.slice.call(arguments, f.length);
}
This was necessary previously, as the arguments object was not a true Array. In addition to being rather clumsy, this method also triggers de-optimization in compilers like V8.
Now, you can do this instead:
function (a, b, ...args) {
// #args is already an Array!
}
The spread pattern is the rest pattern in reverse—you expand a single variable into many:
const week = ['mon','tue','wed','thur','fri'];
const weekend = ['sat','sun'];
console.log([...week, ...weekend]); // ['mon','tue','wed','thur','fri','sat','sun']
week.push(...weekend);
console.log(week); // ['mon','tue','wed','thur','fri','sat','sun']