May '95
Dec '95
Sep '95
1997
Aug '96
1999
1998
2009
2003
2015
2011
2016
Mocha (Brendan Eich, Netscape)
LiveScript
JavaScript
Edition 1
JScript (Microsoft)
Edition 2
Edition 3
Edition 4
Edition 5
Edition 5.1
Edition 6
Edition 7
ECMA-262 specification
/*jshint esnext: true */
import Todo from './todo';
import {values} from './generators';
class TodoList {
constructor() {
this.todos = {};
this.length = 0;
}
add(text, done = false) {
let todo = new Todo(text, done);
this.todos[String(todo.timestamp)] = todo;
this.length++;
}
archiveCompleted() {
for (let todo of values(this.todos)) {
if (todo.done) this.delete(todo);
}
}
delete(todo) {
delete this.todos[String(todo.timestamp)];
this.length--;
}
getUncompletedCount() {
let count = 0;
for (let todo of values(this.todos)) {
if (!todo.done) count++;
}
return count;
}
}
export default TodoList;
/*jshint esnext: true */
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var _todo = require('./todo');
var _todo2 = _interopRequireDefault(_todo);
var _generators = require('./generators');
var TodoList = (function () {
function TodoList() {
_classCallCheck(this, TodoList);
this.todos = {};
this.length = 0;
}
_createClass(TodoList, [{
key: 'add',
value: function add(text) {
var done = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
var todo = new _todo2['default'](text, done);
this.todos[String(todo.timestamp)] = todo;
this.length++;
}
}, {
key: 'archiveCompleted',
value: function archiveCompleted() {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _generators.values)(this.todos)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var todo = _step.value;
if (todo.done) this['delete'](todo);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator['return']) {
_iterator['return']();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
}, {
key: 'delete',
value: function _delete(todo) {
delete this.todos[String(todo.timestamp)];
this.length--;
}
}, {
key: 'getUncompletedCount',
value: function getUncompletedCount() {
var count = 0;
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = (0, _generators.values)(this.todos)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var todo = _step2.value;
if (!todo.done) count++;
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2['return']) {
_iterator2['return']();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
return count;
}
}]);
return TodoList;
})();
exports['default'] = TodoList;
module.exports = exports['default'];
ES6
ES5
function Client (firstname, lastname) {
this.id = undefined
this.firstname = firstname
this.lastname = lastname
}
Client.prototype.getFullName = function () {
return this.firstname + this.lastname
}
Client.prototype.setId = function () {
this.id = generateGuid()
}
ES5
ES6
class Client {
constructor(firstname, lastname) {
this.id = undefined
this.firstname = firstname
this.lastname = lastname
}
getFullName() {
return this.firstname + this.lastname
}
generateId() {
this.id = generateGuid()
}
}
ES6
class Client {
constructor(firstname, lastname) {
this.id = undefined
this.firstname = firstname
this.lastname = lastname
}
getFullName() {
return this.firstname + this.lastname
}
generateId() {
this.id = generateGuid()
}
}
ES6
class Client {
constructor(firstname, lastname) {
this.id = undefined
this.firstname = firstname
this.lastname = lastname
}
getFullName() {
return this.firstname + this.lastname
}
generateId() {
this.id = generateGuid()
}
static generateGuid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1)
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4()
}
}
ES6 introduces the 'extend' keyword
class TCSClient extends Client {
this.evalSystem = "TCS"
}
var TCSClient = Object.create(Client);
TCSClient.evalSystem = "TCS"
ES6
ES5
Can override functions by re-implementing them in child class
class TCSClient extends Client {
getFullName() {
return this.firstname + " " +
this.lastname
}
}
var TCSClient = Object.create(Client)
TCSClient.prototype.getFullName() {
return this.firstname + " " +
this.lastname
}
ES5
ES6
ES5
function Client () {
this.vcNumber = undefined;
this.vcNumberPrefix = "";
}
Client.prototype.generateVCNumber = function () {
var number = Math.floor(Math.random() * 10000000);
this.vcNumber = this.vcNumberPrefix + pad(number, 7);
}
var TCSClient = Object.create(Client);
TCSClient.vcNumberPrefix = "A";
TCSClient.prototype.generateVCNumber = function() {
Client.prototype.generateVCNumber.call(this);
}
Can access methods of the parent object using super()
ES6
class DRClient extends Client {
this.vcNumberPrefix = "W";
generateVCNumber() {
super.generateVCNumber();
}
}
Only one default export per module
// ClientService.js
class ClientService {
clients = [];
addClient(client) {
this.clients.add(client);
}
}
export default ClientService;
import ClientService from './ClientService';
class ClientComponent {
submitClient(client) {
ClientService.addClient(client);
}
}
// Constants.js
var $BANKRUPTCY_MIN_DEBT = 1001;
var $SEQUESTRATION_MIN_DEBT = 3000;
export $BANKRUPTCY_MIN_DEBT;
export $SEQUESTRATION_MIN_DEBT;
// Constants.js
var $BANKRUPTCY_MIN_DEBT = 1001;
var $SEQUESTRATION_MIN_DEBT = 3000;
export { $BANKRUPTCY_MIN_DEBT,
$SEQUESTRATION_MIN_DEBT}
or
// Constants.js
var $BANKRUPTCY_MIN_DEBT = 1001;
var $SEQUESTRATION_MIN_DEBT = 3000;
export $BANKRUPTCY_MIN_DEBT;
export $SEQUESTRATION_MIN_DEBT;
import {$BANKRUPTCY_MIN_DEBT,
$SEQUESTRATION_MIN_DEBT} from './Constants';
function checkSomething(
return $BANKRUPTCY_MIN_DEBT > $SEQUESTRATION_MIN_DEBT;
}
// Constants.js
var $BANKRUPTCY_MIN_DEBT = 1001;
var $SEQUESTRATION_MIN_DEBT = 3000;
export $BANKRUPTCY_MIN_DEBT;
export $SEQUESTRATION_MIN_DEBT;
import {$BANKRUPTCY_MIN_DEBT as minDebt,
$SEQUESTRATION_MIN_DEBT as seq} from './Constants';
function checkSomething(
return minDebt > seq;
}
// IncomesController.js
export var incomeSupport;
export var jobSeekers;
export var incapacity;
export function totalBenefitIncome () {
this.valIncomeSupport +
this.valJobSeekers +
this.valIncapacity;
}
import * as incomes from './IncomesController'
function submitIncomes() {
incomes.incomeSupport = this.valIncomeSupport;
incomes.jobseekers = this.valJobseekers;
incomes.incapacity = this.valIncapacity;
this.valTotalBenefitIncome = incomes.TotalBenefitIncome;
}
New primitive type
Similar to strings:
var mySymbol = Symbol("description");
console.log(typeof mySymbol); // 'symbol'
Unique (even where description is same)
Symbol('foo') === Symbol('foo'); // false
Does not use 'new' operator
var symbol = new Symbol(); // TypeError
if (element.isMoving) {
smoothAnimations(element);
}
element.isMoving = true;
var isMoving = Symbol("isMoving");
...
if (element[isMoving]) {
smoothAnimations(element);
}
element[isMoving] = true;
var myArray = ['a', 'b', 'c'];
var myIterable = myArray[Symbol.iterator]();
> iterable.next()
{ value: 'a', done: false };
> iterable.next()
{ value: 'b', done: false };
> iterable.next()
{ value: 'c', done: false };
> iterable.next()
{ value: undefined, done: true };
Can flag an object as having a set of common methods by attaching certain symbols, e.g.
Iterable objects use the 'well-known' Symbol.iterator
[Symbol.iterator]: () => ({
items: ['f', 'o', 'o'],
next: function next() {
return {
done: this.items.length === 0,
value: this.items.shift()
}
}
})
next() method (no args)
returns Object with done, value
function* myGenerator() {}
// or
function *myGenerator() {}
Generator functions contain 'yield' expressions
function* myGenerator() {
yield 'f';
console.log('o');
yield 'o';
console.log('b');
yield 'a';
console.log('r');
}
var fooBar = myGenerator();
for(var item of fooBar) {
console.log(item); // 'f' 'o' 'o' 'b' 'a' 'r'
}
function *myGenerator(x) {
var y = x + 3;
var z = 1 + (yield(y));
return z;
}
var gen = myGenerator(2);
console.log( gen.next() );
console.log( gen.next(6) );
1
2
3
4
7
6
5
Hoisting - variables and function declarations get pulled to the top of their scope
var foo = true;
function bar() {
if (!foo) {
var foo = "Foo is false!"
};
console.log(foo)
}
bar()
> Foo is false!
var foo;
function bar() {
var foo;
if (!foo) {
foo = "Foo is false!"
};
console.log(foo);
}
foo = true;
bar();
ES5
ES5
Console
var x = 1;
if (x) {
var x = 2;
};
console.log(x);
> 2
{ } blocks do not create a new scope
ES5
What if we want a temporary scope for the inner x?
Console
var x = 1;
if (x) {
(function () {
var x = 2;
})
};
console.log(x);
> 1
ES5
Console
Could create a new function scope
var x = 1;
if (x) {
let x = 2;
}
console.log(x);
> 1
ES6
Console
const stuff = { things: ['chair', 'desk', 'computer'] };
const stuff = { things: ['chair', 'desk', 'computer'] };
stuff = {}; // fails
console.log(stuff.things); // ...
const stuff = { things: ['chair', 'desk', 'computer'] };
stuff.things.push('pot plant');
console.log(stuff.things);
// chair,desk,computer,pot plant
Const
var arrow = function() {}
var arrow = () => {}
becomes
function() {}
() => {}
becomes
function(a) { return a * a }
a => a * a
becomes
function(a, b) { return a + b }
(a, b) => a + b
becomes
function Counter() {
var that = this;
that.seconds = 0;
setInterval(function() {
that.seconds++
}, 1000);
}
function Counter() {
this.seconds = 0;
setInterval(function() {
this.seconds++
}.bind(this), 1000);
}
In ES5, functions create their own scope
ES5
function Counter() {
this.seconds = 0;
setInterval(function() {
this.seconds++
}.bind(this), 1000);
}
function Counter() {
this.seconds = 0;
setInterval(() => this.seconds++, 1000);
}
ES5
ES6
Previously only one collection type in JavaScript (Array)
ES5 maps often created using regular Objects
var hashMap = {};
function add(key, value) {
hashMap[key] = value;
}
function get(key) {
return hashMap[key];
}
add('request', { description: 'Simplified HTTP request client' });
add('moment', { description: 'Parse, validate, manipulate, and display dates' });
add('lodash', { description: 'The modern build of lodash modular utilities' });
var registry = {};
function add(element, meta) {
registry[element] = meta;
}
function get(element) {
return registry[element];
}
add(document.getElementById("wages-panel"), { display: true });
add(document.getElementById("child-income-panel"), { display: false });
add(document.getElementById("benefits-panel"), { display: true });
add(document.getElementById("otherincome-panel"), { display: true });
Problems with this approach:
Maps allow for any type to be used as key or value
var map = new Map();
map.set(new Date(), function today() {});
map.set(() => 'key', { foo: 'bar' });
map.set(Symbol('items'), [1, 2]);
ES5
for (let key in object) {
if (object.hasOwnProperty(key)) {
console.log(key, object[key]);
}
}
for (let key in object) {
if (object.hasOwnProperty(key) &&
typeof object[key] !== "function") {
console.log(key, object[key]);
}
}
ES5
ES5
for (let key of map.keys()) {
console.log(key);
}
for (let value of map.values()) {
console.log(value);
}
for (let entry of map) {
console.log(entry[0], entry[1]);
}
ES6
const privateData = new Map();
class myClass {
constructor (name, age) {
privateData(this, { name: name, age: age });
}
getname () {
return privateData.get(this).name;
}
getAge() {
return privateData.get(this).age;
}
}
export default MyClass;
let answersMap = new WeakMap();
let childBenefit = document.getElementById('child-benefit');
myElement.addEventListener('change', function() {
var answerFrequency = getFrequency();
answersMap.set(childBenefit, {frequency: answerFrequency}
}, false);
let childNumber = document.getElementById('child-number')
if(childNumber.number === 0) {
childBenefit.parentNode.removeChild(childBenefit)
}
console.log(answerMap.get(childBenefit) // undefined
Storing data on a DOM element
var text = `
This is a template literal
'It can span multiple lines'
"And can contain quotation marks"
`
var text = (
'This is the old method\n' +
'for strings spanning\n' +
'several lines'
)
var text = [
'Or',
'perhaps',
'this'
].join('\n')
ES5
ES6
var text = `the time and date is ${new Date().toLocaleString()}`
var a = 6;
console.log(`The number is ${a}`)
Can interpolate expressions with ${expression}
var foo = { bar: 'bar' }
console.log(foo.bar) // 'bar'
var foo = { bar }
console.log(foo.bar) // 'bar'
If property value matches property name, can omit value
ES5
ES6
var foo = 'bar'
var baz = { [foo]: 'qux' }
console.log(baz)
// { bar: 'qux' }
var foo = { [bar + baz]: 'qux' }
console.log(foo)
// { barbaz: 'qux' }
function getAnswerModel (dataItem, amt, frequency) {
return {
['ans_' + dataItem] : {
amount: amt;
frequency: freq
}
}
}
Can compute property names using []
Possible use case:
var foo = {
bar(baz) { }
}
var foo = {
bar: function(baz) { }
}
Declaring methods on an object
ES5
ES6
var clientDetails = {
userName: 'Joe Bloggs',
get name() {
return this.userName
},
set name(value) {
if (!value) {
throw new Error('Name invalid')
}
this.userName= value
}
}
Getters and setters declared on functions
let myObject = { foo:'bar', baz: 'qux' }
let { foo, baz } = myObject
console.log(foo) // 'bar'
console.log(baz) // 'qux'
An object literal lets us create multiple properties at the same time
An object pattern lets us extract multiple properties at the same time
Properties mapped to aliases
let myObject = { foo:'bar', baz: 'qux' }
let { norf: a } = myObject
console.log(a) // undefined
Properties can be mapped to aliases
let { foo : f, baz : b } = myObject
console.log(f) // 'bar'
console.log(b) // 'qux'
Properties not found are saved as undefined
let myObject = { foo: [{ bar: 'baz', qux: 'norf' }] }
let { foo: [{ bar: a }] } = myObject
console.log(a) // 'baz'
let myObject = { foo:'bar', baz: undefined }
let { baz = 'default' } = myObject
console.log(baz) // 'default'
Can define default values if 'pulled' property is undefined
Patterns can be nested arbitrarily deeply
let [, x, y] = [1, 2, 3]
console.log(x) // 2
console.log(y) // 3
let [x, y] = [1, 2, 3]
console.log(x) // 1
console.log(y) // 2
Similarly, for arrays
Can skip over items not cared about
function swapIf () {
var left = 10;
var right = 20;
var aux;
if(right > left) {
aux = right;
right = left;
left = aux;
}
}
function swapIf () {
var left = 10
var right = 20
if(right > left) {
[left, right] = [right, left]
}
}
Can swap variables without need for a temporary variable
ES5
ES6
function displayNameAge ( {name, age} ) {
console.log(`${name} is ${age} years old`)
}
displayNameAge( { age: 27, name: 'John'} ) // 'John is 27 years old'
Can destructure a function's parameter list
function getCoordinates() {
return {
x: 5,
y: 22
}
}
var {x, y} = getCoords()
console.log(x) // 5
console.log(y) // 22
function getCoordinates() {
return {
x: 5,
y: 22
}
}
var object = getCoordinates();
var x = object.x;
var y = object.y;
console.log(x) // 5
console.log(y) // 22
Possible use cases
More terse interaction with returned objects
ES5
ES6
function random() ( {min=1, max=100} ) {
return Math.floor(Math.random() * (max - min) + min
}
console.log(random( {} )) // 67
console.log(random( { max: 20 } ) // 11
Possible use cases
Define default options for a method
Rest parameters - create an array out of a function's arguments
function concat() {
return Array.prototype.slide.call(arguments).join(' ')
}
var result = concat('Lorem', 'ipsum', 'dolor', 'sit', 'amet')
console.log(result) // 'Lorem ipsum dolor sit amet'
function concat(...words) {
return words.join(' ')
}
var result = concat('Lorem', 'ipsum', 'dolor', 'sit', 'amet')
console.log(result) // 'Lorem ipsum dolor sit amet'
ES5
ES6
function concat(prefix, suffix, ...words) {
return prefix + words.join(' ') + suffix
}
var result = concat('START', 'END', 'Lorem', 'ipsum', 'dolor', 'sit', 'amet')
console.log(result) // 'START Lorem ipsum dolor sit amet END'
function concat() {
var words = Array.prototype.slide.call(arguments);
var prefix = words.shift();
var suffix = words.shift();
return prefix + words.join(' ') + suffix;
}
var result = concat('START', 'END', 'Lorem', 'ipsum', 'dolor', 'sit', 'amet')
console.log(result) // 'START Lorem ipsum dolor sit amet END'
If 'arguments' was used, need to cut first two params by shifting
If other parameters, rest param must be rightmost
ES5
ES6
var missing = [4, 5, 6]
var sequence = [1, 2, 3, ...missing, 7, ,8 , 9]
Spread operator
To recap:
var promise = new Promise(function(resolve, reject) {
// do something
if( /* thing was done */ ) {
resolve("Success");
}
else {
reject(Error("Failed"));
}
});
promise.then(function(result) {
// Action on success
}, function(err) {
// Action on error
});
Creating a promise:
Using a promise:
Takes one argument:
then() takes two arguments:
http://habrahabr.ru/post/175371/ Обзор ECMAScript 6, следующей версии JavaScript
http://habrahabr.ru/post/241275/ Ecmascript 6 — что можно использовать уже сейчас
http://es6-features.org/ New Features: Overview & Comparison
http://codecondo.com/learn-ecmascript-6-resources-javascript-devs/
18 Resources on ES6 for JavaScript Developers