Quoi de neuf dans ES2020 ?
Une sélection de trucs cools de Christophe Porteneuve at Paris Web 2017
Une sélection de trucs cools de Christophe Porteneuve at Paris Web 2017
const christophe = {
family: { wife: 'Élodie', son: 'Maxence' },
city: 'Paris, FR',
company: 'Delicious Insights',
trainings: [
'Web Apps Modernes', 'Node.js', 'Git Total',
'ES Total', 'Webpack'
],
webSince: 1995,
claimsToFame: [
'Prototype.js',
'Ruby On Rails',
'Prototype and Script.aculo.us',
'Paris Web',
'NodeSchool Paris'
],
}
L’ECMA est un organisme international de standardisation.
(comme l’ISO, l’IETF ou le W3C, par exemple)
ES = ECMAScript. Le standard officiel de JavaScript*
TC39 = Technical Committee 39. S’occupe de plusieurs standards :
ECMAScript (ECMA-262), Intl (ECMA-402), JSON (ECMA-404), etc
Réunions itinérantes tous les 2 mois. Une version officielle par an, en juin.
« ES6 » ➡️ ES2015, « ES7 » ➡️ ES2016, et désormais ES2017, etc.
Tout est public. Les propositions d’évolutions traversent 5 stades :
Stade | Description |
---|---|
0 Strawman | « Ce serait bien si on avait un opérateur licorne (🦄) pour… » |
1 Proposition | Un membre du TC39 est « champion ». On a déjà débroussaillé la tête qu’aurait le code, l’impact sur l’existant, etc. |
2 Brouillon | Texte initial de la spec, couvre en détail tous les aspects critiques et la sémantique technique de la fonctionnalité. |
3 Candidat | La spec est complète, vérifiée par qui de droit et validée. L’API est finalisée, il ne reste aucune zone d’ombre. |
4 Fini | Couvert par Test262, implémenté en natif par 2+ moteurs répandus (ex. V8 et SpiderMonkey), REX significatif. |
function yowza (
key,
increment,
) {
// …
}
const yowza = (
key,
increment,
) => …
yowza(
'itemScore',
+10,
)
N’oubliez pas que depuis ES5 (2009…) on pouvait déjà faire ça :
const roiDeLaClasse = {
first: 'Georges',
last: 'Abitbol',
}
const courses = [
'ES Total',
'Webpack',
]
async
/ await
async function createEntry(req, res) {
try {
const urlReq = await fetch(req.body.url)
const html = await urlReq.text()
const analysis = unfluff(html)
const entry = await Entry.post({ … })
// …
} catch (err) {
req.flash('error', `Une erreur est survenue en traitant cette URL : ${err.message}`)
res.redirect('/entries/new')
}
}
const [tags, entryCount, entries] = await Promise.all([
Entry.tags(), Entry.count(), Entry.getEntries(req.query),
])
Des files de requêtes faciles… Naturellement basé promesses et async
/await
.
for await (const line of readLines(filePath)) {
console.log(line)
}
async function* readLines(path) {
let file = await fileOpen(path);
try {
while (!file.EOF) {
yield await file.readLine();
}
} finally {
await file.close();
}
}
// nonStandardAction = { type: 'progress', goalId: 1, increment: 2 }
const { type, ...payload } = nonStandardAction
// => payload = { goalId: 1, increment: 2 }
function doStuff (url, options = {}) {
const opts = { ...DEFAULT_OPTIONS, ...options, regular: true }
// …
}
return {
...state,
history: tallyHistory(state),
today: moment().format('YYYY-MM-DD'),
todaysProgress: {},
}
Notez que c’est un nouveau comportement par défaut :
il était déjà possible de définir une itérabilité personnalisée pour nos objets.
class APIConnector extends Component {
// Champ statique (ici, public)
static propTypes = {
apiKey: PropTypes.string.isRequired,
}
// Champ d’instance privé (sans contournement possible) (sémantique "friend" C++)
#secretToken = null
// Champ d’instance public (valeur par défaut avant code du constructeur)
awesome = true
// …
}
import(…)
Permet l’import de modules ES natifs dont le spécificateur n’est pas connu à l’avance
(ex. scenarii de code splitting pour les routes, langues, etc.)
import(`./l10n/${currentUser.locale}`)
.then((locale) => app.l10n.use(locale))
.catch((err) => app.reportError(err))
Encore plus classe :
try {
app.l10n.use(await import(`./l10n/${currentUser.locale}`))
} catch (err) {
app.reportError(err)
}
XRegExp, tes jours sont comptés… 😉
const RE_ISO_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u
console.log(RE_ISO_DATE.exec('2017-10-05').groups) // => { year: '2017', month: '10', day: '05' }
'2017-10-05'.replace(RE_ISO_DATE, '$<day>/$<month>/$<year>') // => '05/10/2017'
const RE_DIGITS = /^\p{Decimal_Number}+$/u
RE_DIGITS.test('𝟏𝟐𝟑𝟜𝟝𝟞𝟩𝟪𝟫𝟬𝟭𝟮𝟯𝟺𝟻𝟼') // => true
const RE_ANYTHING_GOES = /foo.bar/s
RE_ANYTHING_GOES.test('foo\nbar') // => true
La continuation logique des champs privés…
class APIConnector {
get #apiToken () {
…
}
set #apiToken (value) {
…
}
#checkConnectivity () {
…
}
}
Y’a pas de raison qu’on soit un des rares langages sans ! 😠
const FACTOR = 1_000_000_000
const FEE = 123_00
const FRACTION = 0.000_001
const BITS = 0b1010_0001_1000_0101
const FLAG = 0xDEAD_BEEF
throw
à la voléePlus seulement une instruction. Évite des fonctions d’enrobage superflues.
function save (fileName = throw new TypeError('File name required')) {
…
}
function getEncoder (encoding) {
// Oui, c'est mochissime ces ternaires imbriqués, je sais…
const encoder = encoding === 'utf8' ? new UTF8Encoder()
: encoding === 'utf16le' ? new UTF16Encoder(false)
: encoding === 'utf16be' ? new UTF16Encoder(true)
: throw new Error(`Unsupported encoding: ${encoding}`)
…
}
@mixin(SocialNetworkAddictMixin)
class Person {
@deprecate
facepalm () { … }
@throttle('1hr', { leading: true })
checkFacebook () { … }
@memoize('15min')
checkTwitter () { … }
}
class Person {
@computedFrom('firstName', 'lastName')
get fullName () {
return `${this.firstName} ${this.lastName}`
}
}
@connect(mapStateToProps, { logOut })
class SettingsScreen extends Component {
@autobind
openGoalAdder () { … }
@override
componentWillReceiveProps () { … }
}
// Chaînage profond ? OKLM.
const userStreet = user?.address?.street
// Même pour l’indexation indirecte, hein…
const userProp = user?.[propName]
// Méthode optionnelle ? Aucun problème…
iterator.return?.()
protocol RubyStyleEnumerable {
// Symbole(s) à implémenter au cas par cas
_each
// Méthodes (implémentées) par-dessus
cycle (…) { … }
count () { … }
drop (…) { … }
each (…) { … }
map (…) { … }
…
}
class Hash implements RubyStyleEnumerable {
// On implémente le symbole à notre sauce…
[RubyStyleEnumerable._each] () { … }
// …et on a le reste gratuitement 😉
}
Christophe Porteneuve
Les slides sont sur bit.ly/pw-es2020