In ECMAScript 5, one creates all callable entities via functions. ECMAScript 6 has more constructs for doing so. This blog post describes them.
function GuiComponent() { // constructor var that = this; var domNode = ...; domNode.addEventListener('click', function () { console.log('CLICK'); that.handleClick(); // `this` is shadowed }); }ECMAScript 6 has arrow functions [2] that have a more compact syntax and don’t have their own this, their this is lexical:
function GuiComponent() { // constructor var domNode = ...; domNode.addEventListener('click', () => { console.log('CLICK'); this.handleClick(); // `this` not shadowed }); }
function foo(arg1, arg2) { ... }In ECMAScript 6, you’ll const-declare an arrow function:
const foo = (arg1, arg2) => { ... };The problem with function declarations is that they shadow this inside methods and constructors.
But they also have two advantages: First, a function object created by a function declaration always gets a meaningful name, which is useful for debugging. However, ECMAScript 6 engines will probably also assign names to arrow functions, at least in standard scenarios such as the one above.
Second, function declarations are hoisted (moved to the beginning of the current scope). That allows you to call them before they appear in the source code. Here, more discipline is required in ECMAScript 6 and source code will sometimes not look as nice (depending on your taste). However, one important case of calling methods and normal functions that appear later does not change: calling them from other methods and functions (after the callees have been evaluated!).
Ironically, not using function declarations may make things less confusing for newcomers, because they won’t need to understand the difference between function expressions and function declarations [3]. In ECMAScript 5, I’m often seeing code like this, using a function expression instead of a function declaration (even though the latter is considered best practice):
var foo = function (arg1, arg2) { ... };
(function () { // open IIFE var tmp = ...; ... }()); // close IIFEIn ECMAScript 6, you can simply use a block and a let variable declaration:
{ // open block let tmp = ...; ... } // close block
var obj = { myMethod: function (arg1, arg2) { ... } };In ECMAScript 6, you get more compact syntax for defining a method (internally, the result is the same):
let obj = { myMethod(arg1, arg2) { ... } };
// Super-constructor function Point(x, y) { this.x = x; this.y = y; } Point.prototype.toString = function () { return '('+this.x+', '+this.y+')'; }; // Sub-constructor function ColorPoint(x, y, color) { Point.call(this, x, y); this.color = color; } ColorPoint.prototype = Object.create(Point.prototype); ColorPoint.prototype.constructor = ColorPoint; ColorPoint.prototype.toString = function () { return this.color+' '+Point.prototype.toString.call(this); };In ECMAScript 6, you use classes [4] (which have the same method definition syntax as ECMAScript 6 object literals):
// Super-class class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '('+this.x+', '+this.y+')'; } } // Sub-class class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // same as super.constructor(x, y) this.color = color; } toString() { return this.color+' '+super(); } }
function *generatorFunction(arg1, arg2) { ... }Or via a generator method definition (which can also be used in classes):
let obj = { *generatorMethod() { ... } };Generator functions are an unfortunate mix of the old and the new. Like function declarations, they have dynamic this. And there are no generator function expressions.
In my opinion, a better choice would be to replace generator function declarations with generator arrow functions. Or at least to additionally introduce the latter, with an asterisk somewhere. For example:
const generatorFunction = (arg1, arg2) =>* { ... };Alas, this idea has been explicitly rejected for ECMAScript 6, due to syntactic issues.
var $button = $('#myButton'); $button.on('click', function () { this.classList.toggle('clicked'); });If you are using such a library, you have no choice but to use functione expressions. If you are considering using this pattern for your own library then know that you don’t have to. You can always introduce an explicit parameter, instead:
var $button = $('#myButton'); $button.on('click', target => { target.classList.toggle('clicked'); });As an added benefit, the this of the surrounding scope remains accessible.
MyClass.prototype.foo = function (arg1, arg2) { ... };In ECMAScript 6, you can use Object.assign() and a method definition inside an object literal:
Object.assign(MyClass.prototype, { foo(arg1, arg2) { ... } });