// ES3, ES5
var handlers = [];
for (var count = 0; count < 3; count++) {
handlers[count] = function () {
alert(count);
}
}
handlers[0](); // alerts "3"
handlers[1](); // alerts "3"
handlers[2](); // alerts "3"
// ES3, ES5
var handlers = [],
count; // this variable is hoisted
for (count = 0; count < 3; count++) {
handlers[count] = function () {
alert(count);
}
}
handlers[0](); // alerts "3"
// Solution in ES3, ES5
var handlers = [];
for (var count = 0; count < 3; count++) {
(function (i) {
handers[i] = function () {
alert(i)
};
})(count);
}
handlers[0](); // alerts "0"
handlers[1](); // alerts "1"
handlers[2](); // alerts "2"
// ES6
var handlers = [];
for (let count = 0; count < 3; count++) {
handlers[count] = function () {
alert(count);
}
}
handlers[0](); // alerts "0"
let x = 1; // let-definition
let(y = 2, z = 3) { // let-statement
// block where "x", "y" and "z" are visible
}
// scope where only "x" is visible
{ // new block scope
let i = 0; // will be visible within this block
}
let (
privVal = 5,
myPrivate = function () { /* do something private */ }
) {
var something = myPrivate();
function getVal() { return privVal; }
}
// ES3, ES5
for (var x in [3, 4, 5]) {
alert(x); // 0, 1, 2 :-\
}
// ES6
for (let y of [3, 4, 5]) {
alert(y); // 3, 4, 5 :-D
}
for (let z of obj) {
alert(z); // values of the object
}
let k, v;
for (k of keys(obj)) {} // keys of the object
for (k of values(obj)) {} // values of the objects
for ([k, v] of items(o)) {} // pair key-value
// keys ?? => Object.keys or import keys from "@iter"
// values ?? => Object.values or import values from "@iter"
// items ?? => Object.items or import items from "@iter"
// ES6
import iterate from "@iter";
obj[iterate] = function () {
return {
next: function () {
// custom iteration
return nextValue;
}
}
};
for (let v of obj) { ... }
// latest proposal in harmony:iterators
obj.iterator = function () {
return {
next: function () { ... }
}
}
for (let p of obj) { ... }
// ES3, ES5
function ajax (url, data, method) {
if (typeof data === 'undefined') data = {appName: 'campaign'};
if (typeof method === 'undefined') method = 'get';
// data = data || {...};
// method = method || 'get';
// ... and your code begins in here...
}
// ES6
function ajax (url, data = {appName: 'campaign'}, method = 'get') {
// less confusing and cleaner code in here ;-)
}
// ES3, ES5
function foo (x, y /*, rest args */) {
var rest = [].slice.call(arguments, 2);
// (arguments instanceof Array) === false
// now "rest" is a real array
rest.slice( ... );
rest.map( ... );
}
// ES6
function foo (x, y, ...rest /* or ...whatever */) {
// "rest" is a real array!!
for (let i of rest) { ... }
rest.slice( ... );
rest.filter( ... );
rest.map( ... );
}
It gets weirder though...
// ES3, ES5
var ARR_SLICE = Array.prototype.slice;
function MyType() {
var args = ARR_SLICE.call(arguments);
this.processArgs.apply(this, args); // I mean, WTF!? O_O
}
MyType.prototype.processArgs = function (arg1, arg2) {
var rest = ARR_SLICE.slice.call(arguments, 2);
};
// ES6
var MyType = function (...args) {
this.processArgs(...args);
}
MyType.prototype.processArgs = function (arg1, arg2, ...rest) {
// "rest" is an array
}
wait, now it gets better ;-)
// ES6
function storeUserDetails(name, age, country) {
$.ajax('http://www.whatever.com/webservice', {
data: { ... }
success: function () { ... }
});
}
//...
var userInfo = ['Fran', 34, 'Spain'];
// ES3-5 way: storeUserDetails.apply(null, userInfo);
storeUserDetails(...userInfo); // makes more sense :-)
Out of curiosity :-P
// ES6
let points = [1, 2, 3, 4, ...restPoints];
let shape = new Shape(...points); //there is no way in ES3 to do this
// There is a crazy way to do it in ES5
var shape = Shape.apply(Shape.bind(Shape.prototype), points);
// ES3
var arr = [1, 2, 3, 4, 5];
//1. Array filter
//too complicated to put vanilla js in here. Using jQuery:
var mult2 = $.grep(arr, function (val, i) {
return val % 2 == 0;
});
console.log(mult2); // will log [2, 4];
//2. Array map
var pow2 = $.map(arr, function (val, i) {
return Math.pow(val, 2);
});
console.log(pow2); // will log [1, 4, 9, 16, 25];
// ES5
var arr = [1, 2, 3, 4, 5];
//1. Array filter
var mult2 = arr.filter(function (val, i) { return val % 2 == 0; });
//2. Array map
var pow2 = arr.map(function (val, i) { return Math.pow(val, 2); });
Picked up from languages such as Python or CoffeeScript:
// ES6
let arr = [1, 2, 3, 4, 5];
//1. Array filter
let mult2 = [x for (x of arr) if (val % 2 == 0)];
//2. Array map
let pow2 = [Math.pow(x, 2) for x of arr];
let name = 'Franito';
//3. More complex operations
let onlyUCVowels =
[c.toUpperCase() for(c of name) if (c.match(/[aeiou]/))].join('');
console.log(onlyUCVowels); // will log "AIO";
Destructuring arrays
//ES6
let [x, y] = [10, -10, 5];
console.log(x, y); // 10, -10;
let [a, , b] = [1, 2, 3];
console.log(a, b); // 1, 3;
// variables swapping
[a, b] = [b, a];
Destructuring objects
// ES6
let user = {name: 'Fran', age: 34, location: [15, -20]};
let {name: n, age: a, location: l} = user;
console.log(n, a, l[0], l[1]); // Fran, 34, 15, -20
// or
let {name, age, location: [lat, lng]} = user;
console.log(name, age, lat, lng); // Fran, 34, 15, -20
Destructuring of function arguments
// ES3, ES5
function ajax (config) {
var url = config.url,
data = config.data || {},
method = config.method || 'get',
complete = config.complete || new Function;
// this is too "noisy" :-\
}
ajax({
url: 'http://www.whatever.com/method',
data: { ... },
method: 'post',
complete: function () { ... }
});
// ES6
function ajax ({
url: u,
data: d,
method: m,
complete: c
}) {
//now we have "u", "d", "m", "c" variables within this function
}
// or
function ajax ({ url, data, method, complete }) {
console.log(url, data, method, complete);
}
// ES3, ES5
function getTypeId() { return 'passport'; };
var myObj = {
name: 'Fran',
getTypeId: getTypeId,
sayName: function () {
alert('My name is ' + this.name);
}
};
myObj[getTypeId()] = '12341234x';
//TOO MUCH typing, we need something better
// ES6
function getTypeId() { return 'passport'; };
var myObj = {
name: 'Fran',
getTypeId,
sayName() {
alert('My name is ' + this.name);
},
[getTypeId()] = '12341234x'
};
//MUCH better :-)
// ES3, ES5
var MyController = function ($http) {
this.httpService = $http;
};
MyController.prototype = {
constructor: MyController,
ajax: function (settings) {
this.httpService(settings)
.success(this.onSuccess) // There is a problem in here
.error(this.onError) // ...and in here
;
},
onSuccess: function () { ... },
onError: function () { ... }
};
//...
onSuccess: function (response) {
if (response.status == 'OK') {
var data = this.sanitize(response.data); // BAM!! error
}
},
//...
// Solution in ES3
//...
ajax: function (settings) {
var self = this; // scope variable for closure :-/
this.httpService(settings)
.success(function (resp) { self.onSuccess(resp); })
.error(function (resp) { self.onError(resp); });
}
//...
// Solution in ES5
//...
ajax: function (settings) {
this.httpService(settings)
.success((function (resp) { this.onSuccess(resp); }).bind(this))
.error((function (resp) { this.onError(resp); }).bind(this));
}
//...
Copied from CoffeeScript and C#??
// ES6
//...
ajax (settings) {
this.httpService(settings)
.success(resp => this.onSuccess(resp))
.error(resp => this.onSuccess(resp))
;
}
//...
setTimeout(() => {
var now = Date.now();
this.renderTime(now);
}, 1000);
document.addEventListener('click', e => this.onClick(e), false);
// or simply to type less ;-)
(name, age) => alert("My name is ${name} and I'm ${age} years old");
//WOW!!, string interpolation! :-)
New feature in Javascript
// ES6
function* myGenerator() {
// some code here
yield myTask1();
yield myTask2();
yield myTask3();
// more code here
}
let g = myGenerator();
g.next(); // executes myTask1 returning result
g.next(); // executes myTask2 returning result
g.next(); // executes myTask3 returning result
... and what can I do with this ???
Iterate over infinite streams
// ES6
var fibonacci = function* () {
let [prev, curr] = [0, 1];
for (;;) { // infinite iteration
[prev, curr] = [curr, prev + curr];
yield curr;
}
};
for (let val of fibonacci()) {
if (val > 1000) break;
console.log(val); // 1, 2, 3, 5, 8...
}
We can create custom iterators
// ES6
function* keyValue(obj) {
for (let k in obj) {
yield [k, obj[k]];
}
}
let Person = {
name: 'Fran',
age: 34,
country: 'Spain'
}
for (let [key, val] of keyValue(obj)) { ... }
Asynchronous programming
// ES3, ES5
// Callbacks
$.ajax('http://get/user', function (data) {
$.ajax('http://process/user', function (result) {
$.ajax('http://save/user', function (response) {
// Pyramid of doom :-O
});
});
});
// Possible solution in ES3, ES5: hoisting functions out
function getUser() {
$.ajax('http://get/user', processUser);
}
function processUser(result) {
$.ajax('http://process/user', saveUser);
}
function saveUser(response) {
// a bit clearer but still confusing;
}
// ES6
let userTasks = (function* () {
// Coroutines that return promises
yield $.ajax('http://get/user');
yield $.ajax('http://process/user');
yield $.ajax('http://save/user');
})();
let promise;
promise = userTasks.next(); // gets the user
promise = userTasks.next(); // process the user
promise = userTasks.next(); // saves the user
Privacy in ES3
//1. Module pattern
var MyModule = (function () {
//Private scope
var privAttr;
function privFunc() { ... }
return { /* Returns public API */ };
})();
// Another variant
var MyModule;
(function (exports) {
var privAttr,
privFunc = function () { ... };
exports.pubAttr = privFunc();
exports.pubFunc = function () {
returns privAttr;
};
})(MyModule || (MyModule = {}));
//2. Constructor pattern. Priviledged methods
var Person = function (name, age) {
//Private members
var privAttr,
privMethod = function () { ... };
//Public attributes
this.name = name;
this.age = age;
//Priviledged methods, must be defined in the constructor
this.getPrivAttr = function () { return privAttr; };
this.doSomething = function () { privMethod(); };
};
Problem: for every new instance, new memory allocation for each of the private and priviledged methods
// Personal improvement
(function () { // private scope
var privAttr = 'secret value', // NOT a good solution
privKey = '__priv_attr__'; // or some difficult string
function privMethod(age) { ... }
function Person (name, age) {
this[privKey] = 'private'; //not quite private, is it?
}
Person.prototype.pubMethod = function (age) {
this.pubAttr = privMethod.call(this, age);
}
})();
... but not the best since we still need priviledged methods to access private attributes
Privacy in ES5
... hasn't improved much. Although we can now make an attribute be readonly using descriptors// ES5
var myObj = {};
Object.defineProperty(myObj, 'attrName', {
value: 'attrValue', // initial value
get: function () { return value; }, // it's called when reading
set: function (val) { ... }, // it's called when writing
writable: false, // can it be overriden?
enumerable: true, // can it be looped over?
configurable: false // can we change its configuration?
});
//... and also some non-standard
myObj.__defineGetter__('prop', function () { return prop; });
myObj.__defineSetter__('prop', function (v) { ... })
Privacy in ES6, using private Symbols
// ES6
(function () {
let secretKeyAttr = new Symbol(), // unique value, NOT a string!!
secretKeyMethod = new Symbol(); // unique object value
function MyType() {
this[secretKeyAttr] = this[secretKeyMethod]();
}
MyType.prototype[secretKeyMethod] = function () {
return secret_value;
}
})();
var myType = new MyType();
// mytype[???], no-one knows the keys
// console.log(myType) only shows string keys
Gets a bit better using private@ sugar
// ES6
(function () {
private @secretAttr;
private @secretMethod;
function MyType() {
this.@secretAttr = this.@secretMethod();
}
MyType.prototype.@secretMethod = function () {
return secret_value;
}
})();
let myType = new MyType;
console.log(myType); // Empty object {}
Ordered list of values that cannot contain duplicates.
Nothing new if you come from Java or Phyton
// ES6
let items = new Set();
items.add(5);
items.add("5");
items.add(5); // oops, duplicate - this is ignored
console.log(items.size); // 2
let items = new Set([1, 2, 3, 4, 5, 5, 5, 'Fran']);
console.log(items.size); // 6
consooe.log(items.has(2)); // true
items.delete('Fran');
consooe.log(items.size); // 5
let elements = new Set(document.querySelector(".element"));
for (let e of elements) {
// loops over all the elements
}
The basic idea is to map a value to a unique key,
any type of key, even objects.
// ES3, ES5
var myMap = {},
key1 = 'bla',
key2 = document.getElementById("myDiv");
myMap[key1] = 'value1';
myMap[key2] = 'value2'; // key2 == "[Object HTMLDivElement]"
// ES6
let myMap = new Map(),
key1 = 'bla',
key2 = document.getElementById("myDiv");
myMap.set(key1, 'value1');
myMap.set(key2, metadata);
// later
let value = myMap.get('bla'),
meta = myMap.get(document.getElementById("myDiv"));
// ES6. Map shares methods with Set
let myMap = new Map([[key1, val1], [key2, val2]]);
console.log(myMap.has(key1)); // true
console.log(myMmap.get(key1)); // val1
console.log(myMap.size); // 2
myMap.delete(key2);
console.log(myMap.size); // 1
for (let key of myMap.keys()) {
console.log("Key: ${key}");
}
for (let value of myMap.values()) {
console.log("Value: ${value}");
}
for (let item of myMap.items()) {
console.log("Key: ${item[0]}, Value: ${item[1]}");
}
Same as Map objects but the key must be an object.
There is a reason for that...
// ES6
let wMap = new WeakMap(),
element = document.querySelector(".element");
wMap.set(element, "My Value");
console.log(wMap.get(element)); // "My Value"
// later - remove reference
element.parentNode.removeChild(element);
element = null; // the element gets garbage collected
console.log(wMap.size); // 0
A WeakMap holds only a weak reference to a key
... more efficient memory consumption :-)
// ES5, Object meta-programming API
var point3D = { x: 5, y: -10, get z() { return this.x * this.y } };
console.log(Object.getOwnPropertyDescriptor(point3D, 'x'));
// { value: 5, writable: true, enumerable: true, configurable: true }
console.log(Object.getOwnPropertyDescriptor(point3D, 'z'));
/* {
get: function() { return this.x * this.y },
set: undefined,
enumerable: true,
configurable: true
} */
// Creating objects in ES5
var point3D = Object.create(Object.prototype, {
x: { /* descriptor */ },
y: { /* descriptor */ },
z: { /* descriptor */ }
});
Object.preventExtensions(point3D);
// point3D.k = 3; can't add new properties
Object.seal(point3D); // delete point3D.x; can't delete properties
Object.freeze(point3D); // point3D.x = 7; can't assign properties
Proxies in ES5?
// ES5
var Point = function (x, y) { // this is the proxy
var __x__ = x, __y__ = y;
Object.defineProperties(this, {
x: {
get: function() { return __x__; },
set: function(val) { __x__ = val; },
},
y: { ... }
});
};
var myPoint = new Point(1, 2);
... and that's it.
Both "Point" and "myPoint" don't reflect structural changes, such as add, delete attributes and change their properties, made to the other.
Proxies in ES6
// ES6
let proxy = Proxy.create(handler, proto);
/*
proxy.foo => handler.get(proxy, foo)
proxy.foo = val => handler.set(proxy, foo, val)
proxy.foo(1, 2, 3) => handler.get(proxy, foo).apply(proxy, [1, 2, 3])
proxy.get => handler.get(proxy, get)
*/
... but not just property access!!:
/*
foo in proxy => handler.has(proxy, foo)
delete proxy.foo => handler.delete(proxy, foo)
for(var prop in proxy) { ... } => handler.enumerate()
Object.defineProperty(proxy, 'foo', pd) =>
handler.defineProperty('foo', pd)
*/
... AND MUCH MORE!!
... but not quite everything :-(
Function Proxies
// ES6
var funcProxy = Proxy.createFunction(hander, call, construct);
/*
funcProxy(1, 2, 3) => call(1, 2, 3)
new funcProxy(1, 2, 3) => construct(1, 2, 3)
funcProxy.prototype => handler.get(funcProxy, prototype)
*/
The last one is quite interesting,
changing the inheritance behaviour ???
Classes in ES6 is pure syntactic sugar.
It's compiled to prototype-based OO pattern
// ES3, ES5
var MyType = function (arg) { // constructor function
// private members declared with "var"
// public attributes attached to "this"
// privileged methods attached to "this"
}
MyType.prototype = { // prototype pattern
constructor: MyType, // resetting constructor
// prototype members - will be copied into the instance
}
// inheritance in ES5
function MySubType() { /* constructor */ }
MySubType.prototype = Object.create(MyType.prototype); // inheritance
MySubType.prototype.constructor = MySubType; // resetting
MySubType.prototype.pubMembersHere = ...
In ES3 we need little bit of juggling
// inheritance in ES3
function inherit(child, parent, override) {
function F() { this.constructor = child; }
F.prototype = parent.prototype;
child.prototype = new F; // where the magic happens
if (override && typeof override === 'object') {
for (var p in override) {
if (override.hasOwnProperty(p) ) {
child.prototype[p] = override[p];
}
}
}
}
var MySubType = function () { ... };
inherit(MySubType, MyType, { /* new members */ });
// or
MySubType.prototype.pubMember = function () { ... };
Calling super gets crazier :-/
// ES3, ES5
var MySubType = function () {
MyType.call(this /*, args*/); // this one is not too bad
};
inherit(MySubType, MyType, {
pubMember: function () {
MyType.prototype.pubMember.call(this /*, args*/);
//... I mean, WTF!!
}
});
// Improvement
function inherit(child, parent) {
function F() { this.constructor = child }
child.parent = F.prototype = parent.prototype;
child.prototype = new F;
}
// calling super constructor: MySubType.parent.constructor.call(this);
// calling super methods: MySubType.parent.method.call(this);
... and finally classes
// ES6
class MyType { // new keyword "class"
// "strict mode";
private @privAttr; // let @privAttr = new Symbol();
static someProp = true; // MyType.someProp = true;
constructor(arg1, arg2) { // function MyType() {}
public pubAttr = arg1; // this.pubAttr = arg1;
this.@privAttr = arg1 * arg2;
}
pubMethod() { // MyType.prototype.pubMethod = function () {}
return [MyType.someProp, this.@privAttr];
}
}
... obviously it looks much cleaner and shows what you really mean when creating new types, or in this case, classes ;-)
Let's have a look at the inheritance...
// ES6
class MySubType extends MyType { // inherit(MySubType, MyType);
constructor(arg1, arg2) {
super(arg1); // MySubType.parent.constructor.call(this, arg1)
}
pubMethod (arg1) {
super.pubMethod(arg1);
// MySubType.parent.pubMethod.call(this, arg1)
}
get x() {} // Object.defineProperty(this, 'x', { get: function })
set y() {} // Object.defineProperty(this, 'y', { set: function })
}
The questions is: does JavaScript need classes? No, but it definitely needs a cleaner way of defining custom types
// ES3, ES5 - module pattern
var MyModule;
(function (exports) {
// importing from an external module
var privVal = Another.Module.value,
privService = Another.Module.Service;
// exporting functionality
exports.pubVal = privService();
exports.pubService = function () { returns privVal; };
})(MyModule || (MyModule = {}));
console.log(MyModule.pubAttr);
var myService = MyModule.pubService; // importing
myService();
... once again, too much of "noise". A sugar is needed
// ES6
module MyModule { // new keyword "module"
// "strict mode";
// importing from an external module. New keyword "import"
import {privVal, privService} from Another.Module.{value, Service}
// exporting functionality. New keyword "export"
export let pubVal = privService();
export function pubService() { return privVal; }
}
console.log(MyModule.pubAttr);
import myService from MyModule.pubService; // importing
myService();
Sweet, isn't it? :-). Gets a bit better...
More ways of importing
import * from MyModule; // defines "pubVal" and "pubService" vars
import {pubVal, pubService} from MyModule; // destructuring
import {$} from 'https://cdn.jquery.com/jquery.js' // WHAT ?!?! O_O
import {maps, search} from 'https://cdn.google.com/google.js'
... and the referred JavaScript files will actually be loaded by your browser before the code is executed... BAM!!!, non-blocking loading :-)