Apprendre ES2015
Cette page a été traduite par PageTurner AI (bêta). Non approuvée officiellement par le projet. Vous avez trouvé une erreur ? Signaler un problème →
Ce document est initialement issu du remarquable dépôt es6features de Luke Hoban. Allez lui donner une étoile sur GitHub !
N'oubliez pas d'expérimenter ces fonctionnalités dans le REPL en ligne.
Introduction
ECMAScript 2015 est un standard ECMAScript ratifié en juin 2015.
ES2015 constitue une mise à jour majeure du langage, et la première révision importante depuis la standardisation d'ES5 en 2009. L'implémentation de ces fonctionnalités dans les principaux moteurs JavaScript est en cours actuellement.
Consultez le standard ES2015 pour la spécification complète du langage ECMAScript 2015.
Fonctionnalités d'ECMAScript 2015
Fonctions fléchées et lexical this
Les fonctions fléchées sont un raccourci syntaxique utilisant =>. Elles présentent des similarités syntaxiques avec des fonctionnalités équivalentes en C#, Java 8 et CoffeeScript. Elles supportent à la fois des corps d'expression et d'instruction. Contrairement aux fonctions classiques, les fléchées partagent le même this lexical que leur code environnant. Si une fléchée est imbriquée dans une autre fonction, elle hérite de la variable "arguments" de sa fonction parente.
// Expression bodies
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);
// Statement bodies
nums.forEach(v => {
if (v % 5 === 0)
fives.push(v);
});
// Lexical this
var bob = {
_name: "Bob",
_friends: [],
printFriends() {
this._friends.forEach(f =>
console.log(this._name + " knows " + f));
}
};
// Lexical arguments
function square() {
let example = () => {
let numbers = [];
for (let number of arguments) {
numbers.push(number * number);
}
return numbers;
};
return example();
}
square(2, 4, 7.5, 8, 11.5, 21); // returns: [4, 16, 56.25, 64, 132.25, 441]
Classes
Les classes ES2015 sont du sucre syntaxique par-dessus le modèle objet basé prototypes. Cette forme déclarative unique facilite l'utilisation des patrons de classe et favorise l'interopérabilité. Les classes supportent l'héritage prototypique, les appels super, ainsi que les méthodes et constructeurs d'instance ou statiques.
class SkinnedMesh extends THREE.Mesh {
constructor(geometry, materials) {
super(geometry, materials);
this.idMatrix = SkinnedMesh.defaultMatrix();
this.bones = [];
this.boneMatrices = [];
//...
}
update(camera) {
//...
super.update();
}
static defaultMatrix() {
return new THREE.Matrix4();
}
}
Objets littéraux étendus
Les objets littéraux étendent leur syntaxe pour supporter la définition du prototype à la construction, le raccourci d'assignation foo: foo, la définition de méthodes et les appels super. Ensemble, ces améliorations rapprochent les objets littéraux des déclarations de classe, permettant aux designs basés objets de bénéficier des mêmes avantages.
var obj = {
// Sets the prototype. "__proto__" or '__proto__' would also work.
__proto__: theProtoObj,
// Computed property name does not set prototype or trigger early error for
// duplicate __proto__ properties.
['__proto__']: somethingElse,
// Shorthand for ‘handler: handler’
handler,
// Methods
toString() {
// Super calls
return "d " + super.toString();
},
// Computed (dynamic) property names
[ "prop_" + (() => 42)() ]: 42
};
La propriété proto nécessite un support natif et était dépréciée dans les versions précédentes d'ECMAScript. La plupart des moteurs la supportent désormais, mais some do not. Notez également que seuls les web browsers sont tenus de l'implémenter (cf. Annex B). Elle est disponible dans Node.
Template Strings
Les Template Strings offrent du sucre syntaxique pour construire des chaînes. Cela ressemble aux fonctionnalités d'interpolation de Perl, Python et autres. Optionnellement, un tag peut être ajouté pour personnaliser la construction de chaîne, évitant les attaques par injection ou permettant de créer des structures de données complexes à partir de contenu textuel.
// Basic literal string creation
`This is a pretty little template string.`
// Multiline strings
`In ES5 this is
not legal.`
// Interpolate variable bindings
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
// Unescaped template strings
String.raw`In ES5 "\n" is a line-feed.`
// Construct an HTTP request prefix is used to interpret the replacements and construction
GET`http://foo.org/bar?a=${a}&b=${b}
Content-Type: application/json
X-Credentials: ${credentials}
{ "foo": ${foo},
"bar": ${bar}}`(myOnReadyStateChangeHandler);
Déstructuration
La déstructuration permet un binding par pattern matching, supportant les tableaux et objets. Son comportement est fail-soft, similaire à une recherche d'objet standard foo["bar"], produisant undefined lorsque la valeur est absente.
// list matching
var [a, ,b] = [1,2,3];
a === 1;
b === 3;
// object matching
var { op: a, lhs: { op: b }, rhs: c }
= getASTNode()
// object matching shorthand
// binds `op`, `lhs` and `rhs` in scope
var {op, lhs, rhs} = getASTNode()
// Can be used in parameter position
function g({name: x}) {
console.log(x);
}
g({name: 5})
// Fail-soft destructuring
var [a] = [];
a === undefined;
// Fail-soft destructuring with defaults
var [a = 1] = [];
a === 1;
// Destructuring + defaults arguments
function r({x, y, w = 10, h = 10}) {
return x + y + w + h;
}
r({x:1, y:2}) === 23
Default + Rest + Spread
Valeurs par défaut évaluées par l'appelé. Transforme un tableau en arguments consécutifs pour un appel de fonction. Lie les paramètres résiduels dans un tableau. Rest remplace arguments et traite les cas courants plus directement.
function f(x, y=12) {
// y is 12 if not passed (or passed as undefined)
return x + y;
}
f(3) == 15
function f(x, ...y) {
// y is an Array
return x * y.length;
}
f(3, "hello", true) == 6
function f(x, y, z) {
return x + y + z;
}
// Pass each elem of array as argument
f(...[1,2,3]) == 6
Let + Const
Constructions de binding à portée de bloc. let est le nouveau var. const est à assignation unique. Des restrictions statiques empêchent l'utilisation avant assignation.
function f() {
{
let x;
{
// this is ok since it's a block scoped name
const x = "sneaky";
// error, was just defined with `const` above
x = "foo";
}
// this is ok since it was declared with `let`
x = "bar";
// error, already declared above in this block
let x = "inner";
}
}
Itérateurs + For..Of
Les objets itérateurs permettent une itération personnalisée comme IEnumerable de CLR ou Iterable de Java. Ils généralisent for..in à une itération basée sur des itérateurs personnalisés avec for..of. Ne nécessitent pas la matérialisation d'un tableau, permettant des modèles de conception paresseux comme LINQ.
let fibonacci = {
[Symbol.iterator]() {
let pre = 0, cur = 1;
return {
next() {
[pre, cur] = [cur, pre + cur];
return { done: false, value: cur }
}
}
}
}
for (var n of fibonacci) {
// truncate the sequence at 1000
if (n > 1000)
break;
console.log(n);
}
L'itération est basée sur ces interfaces duck-typed (en utilisant la syntaxe TypeScript uniquement à titre d'illustration) :
interface IteratorResult {
done: boolean;
value: any;
}
interface Iterator {
next(): IteratorResult;
}
interface Iterable {
[Symbol.iterator](): Iterator
}
Pour utiliser les itérateurs, vous devez inclure le polyfill Babel.
Générateurs
Les générateurs simplifient la création d'itérateurs avec function* et yield. Une fonction déclarée avec function* retourne une instance Generator. Les générateurs sont des sous-types d'itérateurs incluant des méthodes supplémentaires next et throw. Ils permettent aux valeurs de remonter dans le générateur, faisant de yield une forme d'expression qui retourne une valeur (ou lance une exception).
Note : Peut également permettre une programmation asynchrone de type 'await', voir aussi la proposition ES7 await proposal.
var fibonacci = {
[Symbol.iterator]: function*() {
var pre = 0, cur = 1;
for (;;) {
var temp = pre;
pre = cur;
cur += temp;
yield cur;
}
}
}
for (var n of fibonacci) {
// truncate the sequence at 1000
if (n > 1000)
break;
console.log(n);
}
L'interface générateur est (en utilisant la syntaxe TypeScript à titre d'illustration) :
interface Generator extends Iterator {
next(value?: any): IteratorResult;
throw(exception: any);
}
Pour utiliser les générateurs, vous devez inclure le polyfill Babel.
Comprehensions
Retiré dans Babel 6.0
Unicode
Ajouts non cassants pour supporter Unicode complet, incluant une nouvelle forme littérale unicode dans les chaînes et le mode u RegExp pour gérer les points de code, ainsi que de nouvelles API pour traiter les chaînes au niveau des points de code 21 bits. Ces ajouts permettent de créer des applications globales en JavaScript.
// same as ES5.1
"𠮷".length == 2
// new RegExp behaviour, opt-in ‘u’
"𠮷".match(/./u)[0].length == 2
// new form
"\u{20BB7}" == "𠮷"
"𠮷" == "\uD842\uDFB7"
// new String ops
"𠮷".codePointAt(0) == 0x20BB7
// for-of iterates code points
for(var c of "𠮷") {
console.log(c);
}
Modules
Support natif au niveau du langage pour la définition de composants modulaires. Standardise les modèles des chargeurs de modules JavaScript populaires (AMD, CommonJS). Le comportement d'exécution est défini par un chargeur par défaut configuré par l'hôte. Modèle implicitement asynchrone – aucun code ne s'exécute tant que les modules requis ne sont pas disponibles et traités.
// lib/math.js
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;
// app.js
import * as math from "lib/math";
console.log("2π = " + math.sum(math.pi, math.pi));
// otherApp.js
import {sum, pi} from "lib/math";
console.log("2π = " + sum(pi, pi));
Quelques fonctionnalités supplémentaires incluent export default et export * :
// lib/mathplusplus.js
export * from "lib/math";
export var e = 2.71828182846;
export default function(x) {
return Math.exp(x);
}
// app.js
import exp, {pi, e} from "lib/mathplusplus";
console.log("e^π = " + exp(pi));
Babel peut transpiler les modules ES2015 vers plusieurs formats différents incluant Common.js, AMD, System et UMD. Vous pouvez même créer le vôtre. Pour plus de détails, consultez la modules docs.
Chargeurs de modules
Ceci est laissé comme défini par l'implémentation dans la spécification ECMAScript 2015. Le standard final fera partie de la Loader specification du WHATWG, actuellement en cours d'élaboration. Ce qui suit provient d'un brouillon précédent d'ES2015.
Les chargeurs de modules prennent en charge :
-
Chargement dynamique
-
Isolation d'état
-
Isolation de l'espace de noms global
-
Hooks de compilation
-
Virtualisation imbriquée
Le chargeur de modules par défaut peut être configuré, et de nouveaux chargeurs peuvent être construits pour évaluer et charger du code dans des contextes isolés ou contraints.
// Dynamic loading – ‘System’ is default loader
System.import("lib/math").then(function(m) {
alert("2π = " + m.sum(m.pi, m.pi));
});
// Create execution sandboxes – new Loaders
var loader = new Loader({
global: fixup(window) // replace ‘console.log’
});
loader.eval("console.log(\"hello world!\");");
// Directly manipulate module cache
System.get("jquery");
System.set("jquery", Module({$: $})); // WARNING: not yet finalized
Comme Babel utilise par défaut des modules common.js, il n'inclut pas le polyfill pour l'API de chargeur de modules. Obtenez-le here.
Pour cela, vous devrez indiquer à Babel d'utiliser le formateur de modules system. Consultez aussi System.js.
Map + Set + WeakMap + WeakSet
Structures de données efficaces pour les algorithmes courants. Les WeakMaps fournissent des tables de correspondance à clés d'objet sans fuites de mémoire.
// Sets
var s = new Set();
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello") === true;
// Maps
var m = new Map();
m.set("hello", 42);
m.set(s, 34);
m.get(s) == 34;
// Weak Maps
var wm = new WeakMap();
wm.set(s, { extra: 42 });
wm.size === undefined
// Weak Sets
var ws = new WeakSet();
ws.add({ data: 42 });
// Because the added object has no other references, it will not be held in the set
Pour prendre en charge Maps, Sets, WeakMaps et WeakSets dans tous les environnements, vous devez inclure le polyfill de Babel.
Proxies
Les Proxies permettent de créer des objets avec toute la gamme de comportements disponibles pour les objets hôtes. Ils peuvent être utilisés pour l'interception, la virtualisation d'objets, la journalisation/profiling, etc.
// Proxying a normal object
var target = {};
var handler = {
get: function (receiver, name) {
return `Hello, ${name}!`;
}
};
var p = new Proxy(target, handler);
p.world === "Hello, world!";
// Proxying a function object
var target = function () { return "I am the target"; };
var handler = {
apply: function (receiver, ...args) {
return "I am the proxy";
}
};
var p = new Proxy(target, handler);
p() === "I am the proxy";
Il existe des pièges disponibles pour toutes les méta-opérations au niveau de l'exécution :
var handler =
{
// target.prop
get: ...,
// target.prop = value
set: ...,
// 'prop' in target
has: ...,
// delete target.prop
deleteProperty: ...,
// target(...args)
apply: ...,
// new target(...args)
construct: ...,
// Object.getOwnPropertyDescriptor(target, 'prop')
getOwnPropertyDescriptor: ...,
// Object.defineProperty(target, 'prop', descriptor)
defineProperty: ...,
// Object.getPrototypeOf(target), Reflect.getPrototypeOf(target),
// target.__proto__, object.isPrototypeOf(target), object instanceof target
getPrototypeOf: ...,
// Object.setPrototypeOf(target), Reflect.setPrototypeOf(target)
setPrototypeOf: ...,
// Object.keys(target)
ownKeys: ...,
// Object.preventExtensions(target)
preventExtensions: ...,
// Object.isExtensible(target)
isExtensible :...
}
En raison des limitations d'ES5, les Proxies ne peuvent pas être transpilés ou polyfillés. Voir le support dans various JavaScript engines.
Symbols
Les Symboles permettent de contrôler l'accès à l'état d'un objet. Ils autorisent les propriétés à être indexées par des string (comme en ES5) ou des symbol. Les Symboles sont un nouveau type primitif. Le paramètre optionnel name sert au débogage mais ne fait pas partie de l'identité. Les Symboles sont uniques (comme des gensym), mais non privés car exposés via des fonctionnalités de réflexion comme Object.getOwnPropertySymbols.
(function() {
// module scoped symbol
var key = Symbol("key");
function MyClass(privateData) {
this[key] = privateData;
}
MyClass.prototype = {
doStuff: function() {
... this[key] ...
}
};
// Limited support from Babel, full support requires native implementation.
typeof key === "symbol"
})();
var c = new MyClass("hello")
c["key"] === undefined
Le support limité nécessite le polyfill de Babel. En raison des limitations du langage, certaines fonctionnalités ne peuvent être transpilées ou polyfillées. Consultez la caveats section de core.js pour plus de détails.
Classes intégrées pouvant être sous-classées
Dans ES2015, les classes intégrées comme Array, Date et les Elements DOM peuvent être sous-classées.
// User code of Array subclass
class MyArray extends Array {
constructor(...args) { super(...args); }
}
var arr = new MyArray();
arr[1] = 12;
arr.length == 2
La sous-classabilité des intégrées doit être évaluée au cas par cas : des classes comme HTMLElement can être sous-classées alors que Date, Array et Error cannot l'être en raison des limitations des moteurs ES5.
API Math + Number + String + Object
De nombreuses additions aux bibliothèques, incluant des bibliothèques Math de base, des helpers de conversion de tableaux et Object.assign pour la copie.
Number.EPSILON
Number.isInteger(Infinity) // false
Number.isNaN("NaN") // false
Math.acosh(3) // 1.762747174039086
Math.hypot(3, 4) // 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2
"abcde".includes("cd") // true
"abc".repeat(3) // "abcabcabc"
Array.from(document.querySelectorAll("*")) // Returns a real Array
Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior
[0, 0, 0].fill(7, 1) // [0,7,7]
[1,2,3].findIndex(x => x == 2) // 1
["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
["a", "b", "c"].keys() // iterator 0, 1, 2
["a", "b", "c"].values() // iterator "a", "b", "c"
Object.assign(Point, { origin: new Point(0,0) })
Littéraux binaires et octaux
Deux nouvelles formes de littéraux numériques sont ajoutées pour le binaire (b) et l'octal (o).
0b111110111 === 503 // true
0o767 === 503 // true
Babel ne peut transformer que 0o767 et non Number("0o767").
Promises
Les Promesses sont une bibliothèque pour la programmation asynchrone. Elles représentent de première classe une valeur pouvant être disponible ultérieurement. Les Promesses sont utilisées dans de nombreuses bibliothèques JavaScript existantes.
function timeout(duration = 0) {
return new Promise((resolve, reject) => {
setTimeout(resolve, duration);
})
}
var p = timeout(1000).then(() => {
return timeout(2000);
}).then(() => {
throw new Error("hmm");
}).catch(err => {
return Promise.all([timeout(100), timeout(200)]);
})
Pour prendre en charge les Promesses, vous devez inclure le polyfill de Babel.
API Reflect
Une API de réflexion complète exposant les méta-opérations au niveau de l'exécution sur les objets. Il s'agit effectivement de l'inverse de l'API Proxy, permettant d'effectuer des appels correspondant aux mêmes méta-opérations que les pièges (traps) du proxy. Particulièrement utile pour implémenter des proxys.
var O = {a: 1};
Object.defineProperty(O, 'b', {value: 2});
O[Symbol('c')] = 3;
Reflect.ownKeys(O); // ['a', 'b', Symbol(c)]
function C(a, b){
this.c = a + b;
}
var instance = Reflect.construct(C, [20, 22]);
instance.c; // 42
Pour utiliser l'API Reflect, vous devez inclure le polyfill de Babel.
Appels terminaux (Tail Calls)
Les appels en position terminale sont garantis de ne pas faire croître la pile de manière illimitée. Cela rend les algorithmes récursifs sûrs face à des entrées non bornées.
function factorial(n, acc = 1) {
"use strict";
if (n <= 1) return acc;
return factorial(n - 1, n * acc);
}
// Stack overflow in most implementations today,
// but safe on arbitrary inputs in ES2015
factorial(100000)
Seule la récursion terminale auto-référencée explicite était prise en charge en raison de la complexité et de l'impact sur les performances d'une prise en charge globale des appels terminaux. Supprimée en raison d'autres bogues et sera réimplémentée.