Commonly referred to as ES6, ECMAScript 2015 is the first major update to the JavaScript language since 2009. JavaScript was created in 10 days in May, 1995 by Brandan Eich while working at Netscape. The lanuage was originally known as Mocha, but was named JavaScript due to the rising popularity of Java, in a rather unfortunate marketing ploy.
Ecma International, formerly known as the European Computer Manufacturers Association, is the standards organization now responsible for shepherding the JavaScript language through its development and official implementation. The "ES" in "ES6" refers to ECMAScript, the official, trademarked name for JavaScript, which comes from this standards organization.
JavaScript was first officially adopted as a standardized API published by ECMA in June of 1997. Since that time, several revisions of the specification have been released to the public:
- Edition 1, June 1997 - the first edition
- Edition 2, June 1998 - updates to bring into full spec compliance
- Edition 3, December 1999 - adds regex, better string and error handling, and control flow
- Edition 4, Abandoned - 10 years passed without standards body agreement
- Edition 5, December 2009 - The modern form of JavaScript most of use are primarily familiar with, updates are primarily to reduce ambiguity and modernize language to bring it in line with how it was being used in the real world
- Edition 5.1, June 2011 - updates to spec
- Edition 6, June 2015 - Significant additions to the standard library for writing mature applications
Edition 7 was published in June of 2016, and edition 8 has been proposed but not officially published. These editions continue to focus on maturing the language into a serious programming language suitable for things far beyond the reach of a lowly click handler.
ES6 contains a large new set of features, some of which have yet to be fully implemented by the major browser vendors. Many of the features we discuss today may not work out of the box in some or all browsers. A good resource for up-to-date browser compliance information is caniuse.com.
If you are writing an application using a modern JS framework, like Ember using ember-cli, or are using a home-grown front end build tool chain of some kind, a great way to be sure your ES6 code will work in most browsers is to transpile the raw ES6 source down into plain JavaScript using Babel.
Without such a transpilation step, some newer language features may simply break, depending on the browser or platform version your code is authored for.
To play, the quickest way to get started exploring might be using an online code prototyping tool like CodePen. Be sure to turn on the Babel "preprocessor" in the JS settings panel.
Constants are what they sound like. Just like in other languages, once a new
constant
is set, any attempt to alter the value of the constant
will throw
an exception at runtime.
Very useful when doing complex numbery stuff, setting versions, or defining any other kind of value that should never be alterable at runtime due to user or program input.
const foo = 'Can\'t touch this.';
foo = 'Ouch!'; // Will throw an exception, and leave poor foo unscathed.
console.log(foo); // -> 'Can\'t touch this.'
The let
keyword is a lot like var
, except that it provides what is known as
"block level scoping". In JavaScript, variables are scoped to the body of the
function or object they are defined within. A parent cannot see into its children,
but a child can see its parents' variables.
Variables created and assigned by let
have the special properties of:
- Being scoped to within the nearest set of curly braces
- Not being subject to variable hoisting rules
let turbo = 'yeppers';
{
console.log(turbo); // -> 'yeppers';
}
{
let turbo = 8; // -> totally different variable from previous turbo
let heidi = 'hidden';
}
console.log(heidi); // Throws a ReferenceError
console.log(turbo); // -> 'yeppers';
function foo() {
console.log(kitten);
var kitten = 5;
}
foo();
// -> 'undefined';
// -> 'undefined';
function bar() {
console.log(doggie);
let doggie = 'Ralph';
}
bar();
// -> Throws a ReferenceError, because `doggie` isn't hoisted
Available in ES5, will work without transpilation in all modern browsers.
The arguments
object is an array-like object available within the body of any
JavaScript function. It contains the list of arguments passed into the function
when it is called. Note that it is not a normal array, and may not behave as
expected if used as such. It is safest to treat it as a read-only value for
inspection and debugging, or to pass to another function in full.
function logArgs() {
console.log(arguments);
}
logArgs(1, 'foo', {}); // -> [1, 'foo', {}];
Fat arrow functions are a new way to define a function in ES6. The aren't a total
analong to named or anonymous functions, however. They differ in that their lexical
scoping retains the this
context of their immediate parent, making them very
useful for nested callbacks, and "functional" styles of writing JavaScript.
See this Twiddle for a demonstration.
import Ember from 'ember';
export default Ember.Component.extend({
foo: 'bar',
init() {
this._super(...arguments);
$('body').on('click', () => {
console.log(this.get('foo'));
// Will work as expected, retaining the "this" context of the enclosing scope
});
$('body').on('click', function() {
console.log(this.get('foo'));
// Will totally fail, "get" is not a function
});
}
});
Another great way to use fat arrow functions is to harness another special property of this syntax: implicit return values. When a fat arrow function is invoked as a one-line declaration, without the use of curly braces, the body of the function's body is implicit.
This implicit return style comes in extremely handy when iterating over an array, or performing very simple operations.
Note: the arguments
object is not available in fat arrow functions.
var doStuffWithExplicitReturn = () => {
return 'dammit grandma get off the phone';
}
doStuffWithExplicitReturn(); // -> 'dammit grandma get off the phone';
var doStuffWithImplicitReturn = () => 6 * 3;
doStuffWithImplicitReturn(); // -> 18;
Available in ES5, will work without transpilation in all modern browsers.
Array.prototype.map
is a built-in array method that takes a function as its
first argument, and returns a new array. That function will be called once per
every item in the array, and will be called with that item as its first argument.
The new array will contain whatever the function returns.
var tacos = ['carnitas', 'pollo', 'al pastor', 'chicharron', 'pescado'];
var holdTheFish = tacos.filter((taco) => taco !== 'pescado');
console.log(holdTheFish); // -> ['carnitas', 'pollo', 'al pastor', 'chicharron'];
Available in ES5, will work without transpilation in all modern browsers.
Array.prototype.filter
is a built-in array method that takes a function as its
first argument, and returns a new array. That function will be called once per
every item in the array, and will be called with that item as its first argument.
If that function returns true
, the new array will retain the element. If it
returns false
, it will not retain the element.
var holdTheFish = ['carnitas', 'pollo', 'al pastor', 'chicharron'];
var prefixedTacos = holdTheFish.map((taco) => "TACO!: " + taco);
console.log(prefixedTacos); // -> ["TACO!: carnitas", "TACO!: pollo", "TACO!: al pastor", "TACO!: chicharron"];
Available in ES5, will work without transpilation in all modern browsers.
Array.prototype.reduce
is a built-in array method that takes a function as its
first argument, and an optional initial value as its second. It calls the
callback for each element in the array, and returns a single value that is the
result of the full set of operations.
[1, 2, 3, 4, 5].reduce(function(accumulator, currentValue, currentIndex, array) {
console.log(arguments);
return accumulator + currentValue;
}, 0); // -> 15
// [0, 1, 0, Array[5]]
// [1, 2, 1, Array[5]]
// [3, 3, 2, Array[5]]
// [6, 4, 3, Array[5]]
// [10, 5, 4, Array[5]]
ES6 provides a new syntax for writing long template strings with interpolated values. The enclosing delimiter is the back-tick character (`), and values can be interpolated using curly braces.
var adjective = 'smelly';
var noun = 'Cheetos thief';
var pluralNoun = 'uncles';
var place = 'gas station';
var madlib = `The ${adjective} ${noun} stole my ${pluralNoun} from the ${place}`;
console.log(madlib); // -> "The smelly Cheetos thief stole my uncles from the gas station";
Default parameters in JavaScript have finally landed. They work much like PHP's own implementation of the same thing.
function sayStuff(toWhom, thingToSay = 'moo') {
console.log(`Hey ${String(toWhom)}! Guess what? ${String(thingToSay).toUpperCase()}!`);
}
sayStuff('Kevin Spacey'); // -> "Hey Kevin Spacey! Guess what? MOO!";
The spread operator allows an array or array-like object to be split into a comma-separated list, such as one might use to define an array literal, or pass arguments into a function.
function sumOfArgumentsAndFive() {
return [5, ...arguments].reduce((a, b) => {
return a + b;
}, 0);
}
console.log(sumOfArgumentsAndFive(1, 2, 3, 4)); // -> 15;
The rest parameter syntax allows the definition of variadic functions in
JavaScript, or a function with indefinite arity, with a named array-like object
containing the optional arguments passed to the function. It is syntactically
quite similar to the spread operator
.
It has always been allowable to pass any number of arguments into a function in JavaScript, whether they are defined with named parameters or not. The benefit of this syntax is to provide a known name to the additional arguments that may be passed.
function logRest(firstArg, secondArg, ...theRestOfThem) {
console.log([...theRestOfThem]);
}
logRest('hi', 'there', 'this', 'is', 'cool'); // -> ['this', 'is', 'cool'];
Destructuring
is a way to simplify extraction of values from arrays or objects.
It is easiest to understand by seeing an example.
let exampleArray = ['Brian Jonestown Massacre', 'Slowdive', 'My Bloody Valentine', 'Creed'];
let [goodband1, goodband2, goodband3, noNoNotInMyCar] = exampleArray;
console.log(goodband3); // -> "Slowdive";
let exampleObject = {
potatoes: 'mashed',
carrots: 'pureed'
};
let {
potatoes,
orange
} = exampleObject;
console.log([potatoes, orange]); // -> ['mashed', 'undefined'];
ES6 modules make it much easier to manage code reuse and sharing. A single ES6
module exposes its content to the world for use only via explicit export
statments. Exported values from a module must either take a specific name, or
must be exported as default
. Like destructuring in JavaScript, an example is
easier to understand than an explanation.
function doStuff() {
console.log('doing stuff.');
}
export { doStuff }; // Single named export
export { doStuff as mainFunc }; // Aliased named export
export default let myVar = 2; // Default export
ES6 modules also provide a new syntax to import
the various exports
from
another module. This syntax, like for export
, is best seen:
import { doStuff, mainFunc } from 'example-module';
import ExampleModule from 'example-module';
doStuff(); // -> "doing stuff.";
mainFunc(); // -> "doing stuff.";
console.log(ExampleModule); // -> 2;