📜 ⬆️ ⬇️

EcmaScript 10 - JavaScript This Year

Standardization JS has moved to a year-long update cycle, and the beginning of the year is a great time to find out what awaits us in the anniversary - the tenth edition of EcmaScript!


ES 9 is the current version of the specification .


ES 10 is still a draft .


Today in Stage 4 # there are only a few sentences.


And in Stage 3 # - a whole dozen!


Of these, in my opinion, the most interesting are private class fields # , shebang grammar for scripts # , arbitrary precision numbers # , access to the global context # and dynamic imports # .



KDPV: Yellow magnet with the inscription "JS ES10" on the screen - from kasper.green & elfafeya.art
Photo by: kasper.green; Yellow Magnet: elfafeya.art & kasper.green


Content


Five stages


Stage 4 - Final #


catch - the argument has become optional # ;


Symbol().description - accessor to the description of the # symbol ;


'строки EcmaScript' - improved compatibility with JSON format # ;


.toString() - prototype method updated # .




Stage 3 - Pre-release #


# - private all classes, through # ;


#!/usr/bin/env node - shebang grammar for scripts # ;


BigInt() is a new primitive, for arbitrary precision numbers # ;


globalThis - a new way to access the global context # ;


import(dynamic) - dynamic import # ;


import.meta - meta-information about the loaded module # ;


Object.fromEntries() - creating an object from an array of pairs - key \ value # ;


JSON.stringify() - fix method # ;


RegExp - outdated features # ;


.trimStart() and .trimEnd() - prototype methods for strings # ;


.matchAll() - .match() with the global flag # ;


.flat() and .flatMap() - prototype methods of arrays # .


Results




Five stages


Stage 0Strawman Basting An Idea that can be implemented through the Babel plugin .;


Stage 1Proposal Suggestion Testing the viability of an idea .;


Stage 2Draft Draft Begin the development of the specification .;


Stage 3Candidate Candidate Preliminary version of the specification .;


Stage 4 ֍ Finished The final version of the specification for this year has been completed.




We will consider only Stage 4 - the de facto standard.


And Stage 3 - which is about to become part of it.





֍ Stage 4


These changes are already included in the standard.


Optional argument of catch


https://github.com/tc39/proposal-optional-catch-binding


Before ES 10, a catch required a mandatory argument to collect error information, even if it is not used:


 function isValidJSON(text) { try { JSON.parse(text); return true; } catch(unusedVariable) { // переменная не используется return false; } } 


Edge is not yet updated to ES 10, and is expected to fail with an error


Starting from the ES 10 edition, the parentheses can be omitted and the catch will look like two drops of water similar to try .



My Chrome has already been updated to ES 10, and in some places to Stage 3 . Further screenshots will be from Chrome


source
 function isValidJSON(text) { try { JSON.parse(text); return true; } catch { // без аргумента return false; } } 



Access to the symbolic link description


https://tc39.imtqy.com/proposal-Symbol-description/


The description of a symbolic link can be indirectly obtained by the toString () method:


 const symbol_link = Symbol("Symbol description") String(symbol_link) // "Symbol(Symbol description)" 

Starting with ES 10, characters now have a description property that is read-only. It allows you to get a description of the symbol without any dancing with a tambourine:


 symbol_link.description // "Symbol description" 

If the description is not specified, it will be returned - undefined :


 const without_description_symbol_link = Symbol() without_description_symbol_link.description // undefined const empty_description_symbol_link = Symbol('') empty_description_symbol_link.description // "" 



JSON compatible EcmaScript strings


https://github.com/tc39/proposal-json-superset


EcmaScript to the tenth edition claims that JSON is a subset of JSON.parse , but this is not true.


JSON lines can contain unshielded delimiter characters for U+2028 LINE SEPARATOR lines and U+2029 PARAGRAPH SEPARATOR paragraphs .


ECMAScript lines up to version 10 are not.


If you call eval() with the string "\u2029" in Edge ,
it behaves as if we had done a line break - right in the middle of the code:




C ES 10 lines - everything is in order:





Completion of the prototype method .toString()


http://tc39.imtqy.com/Function-prototype-toString-revision/


Goals change
  • remove the incompatible requirement:

If the implementation cannot create a source line that matches these criteria, it should return a string for which eval will throw an exception with a syntax error.

  • clarify the “functionally equivalent” requirement;


  • standardize string representation of built-in functions and host objects;


  • clarify the presentation requirements based on the “actual characteristics” of the object;


  • make sure that parsing the string contains the same function body and parameter list as the original;


  • for functions defined using ECMAScript code, toString should return a fragment of the source text from the beginning of the first token to the end of the last token corresponding to the corresponding grammatical structure;


  • for built-in functional objects, toStrings should not return anything but NativeFunction;


  • for callees that have not been defined using ECMAScript code, the toString must be returned to NativeFunction;


  • for functions created dynamically (constructors of a function or a generator), toString must synthesize the source text;


  • for all other objects, toString should throw a TypeError exception.



 // Пользовательская функция function () { console.log('My Function!'); }.toString(); // function () { console.log('My Function!'); } // Метод встроенного объекта объект Number.parseInt.toString(); // function parseInt() { [native code] } // Функция с привязкой контекста function () { }.bind(0).toString(); // function () { [native code] } // Встроенные вызываемые функциональный объекты Symbol.toString(); // function Symbol() { [native code] } // Динамически создаваемый функциональный объект Function().toString(); // function anonymous() {} // Динамически создаваемый функциональный объект-генератор function* () { }.toString(); // function* () { } // .call теперь обязательно ждёт, в качестве аргумента, функцию Function.prototype.toString.call({}); // Function.prototype.toString requires that 'this' be a Function" 





֍ Stage 3


Proposals that were released from the draft status, but not yet included in the final version of the standard.


Private \ static \ public methods \ properties \ attributes for classes


https://github.com/tc39/proposal-class-fields
https://github.com/tc39/proposal-private-methods
https://github.com/tc39/proposal-static-class-features


In some languages ​​there is an agreement to call private methods through a visible space (“ _ ” is such a piece, you may know this sign under the wrong name - underscore) .


For example:


 <?php class AdultContent { private $_age = 0; private $_content = '…is dummy example content (•)(•) —3 (.)(.) only for adults…'; function __construct($age) { $this->_age = $age; } function __get($name) { if($name === 'content') { return " (age: ".$this->_age.") → ".$this->_getContent()."\r\n"; } else { return 'without info'; } } private function _getContent() { if($this->_contentIsAllowed()) { return $this->_content; } return 'Sorry. Content not for you.'; } private function _contentIsAllowed() { return $this->_age >= 18; } function __toString() { return $this->content; } } echo "<pre>"; echo strval(new AdultContent(10)); // (age: 10) → Sorry. Content not for you echo strval(new AdultContent(25)); // (age: 25) → …is dummy example content (•)(•) —3 only for adults… $ObjectAdultContent = new AdultContent(32); echo $ObjectAdultContent->content; // (age: 32) → …is dummy example content (•)(•) —3 only for adults… ?> 

Let me remind you - this is only an agreement. Nothing prevents you from using the prefix for other purposes, using another prefix, or not using it at all.


Personally, I am impressed by the idea of ​​using the space character as a prefix for functions that return this . So they can be chained together.


The developers of the EcmaScript specification went further and made the prefix- octotorp (“ # ” —short, hash) part of the syntax.


The previous example in ES 10 can be rewritten as follows:


 export default class AdultContent { // Приватные атрибуты класса #age = 0 #adult_content = '…is dummy example content (•)(•) —3 (.)(.) only for adults…' constructor(age) { this.#setAge(age) } // Статический приватный метод static #userIsAdult(age) { return age > 18 } // Публичное свойство get content () { return `(age: ${this.#age}) → ` + this.#allowed_content } // Приватное свойство get #allowed_content() { if(AdultContent.userIsAdult(this.age)){ return this.#adult_content } else { return 'Sorry. Content not for you.' } } // Приватный метод #setAge(age) { this.#age = age } toString () { return this.#content } } const AdultContentForKid = new AdultContent(10) console.log(String(AdultContentForKid)) // (age: 10) → Sorry. Content not for you. console.log(AdultContentForKid.content) // (age: 10) → Sorry. Content not for you. const AdultContentForAdult = new AdultContent(25) console.log(String(AdultContentForAdult)) // (age: 25) → …is dummy example content (•)(•) —3 (.)(.) only for adults… console.log(AdultContentForAdult.content) // (age: 25) → …is dummy example content (•)(•) —3 (.)(.) only for adults… 

The example is unnecessarily complicated to show private properties, methods, and attributes at once. But in general, JS - pleases the eye with its brevity compared with the PHP option. No private function for you _..., no semicolons at the end of the line, and a dot instead of "->" to go deep into the object.


Named getters. For dynamic names, proxies are objects.


It seems to be little things, but after switching to JS, there is less and less desire to return to PHP.


By the way, private accessors are only available with Babel 7.3.0 and later.


At the time of this writing, the latest version according to npmjs.com is 7.2.2


We are waiting in Stage 4!




Shebang grammar


https://github.com/tc39/proposal-hashbang


Heshbang is a Unicsoid familiar way to specify an interpreter for an executable file:


 #!/usr/bin/env node // в скрипте 'use strict'; console.log(1); 

 #!/usr/bin/env node // в модуле export {}; console.log(1); 

at the moment, on a similar trick, Chrome is throwing SyntaxError: Invalid or&nbsp;unexpected token


Big numbers with BigInt


https://github.com/tc39/proposal-bigint


browser support

Browser support for the primitive BigInt ()


The maximum integer that can be safely used in JavaScript (2⁵³ - 1):


 console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991 

BigInt is needed to use arbitrary precision numbers.


This type is declared in several ways:


 // используя 'n' постфикс в конце более длинных чисел 910000000000000100500n // 910000000000000100500n // напрямую передав в конструктор примитива BigInt() без постфикса BigInt( 910000000000000200500 ) // 910000000000000200500n // или передав строку в тот-же конструктор BigInt( "910000000000000300500" ) // 910000000000000300500n // пример очень большого числа длиной 1642 знака BigInt( "" ) \\ 

This is a new primitive type:


 typeof 123; // → 'number' typeof 123n; // → 'bigint' 

It can be compared with ordinary numbers:


 42n === BigInt(42); // → true 42n == 42; // → true 

But mathematical operations need to be carried out within one type:


 20000000000000n/20n // 1000000000000n 20000000000000n/20 // Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions 

Unary minus is supported, unary plus returns an error:


  -2n // -2n +2n // Uncaught TypeError: Cannot convert a BigInt value to a number 


globalThis - a new way to access the global context


https://github.com/tc39/proposal-global


works in chrome


Since the implementation of the global scope is dependent on a particular engine, you had to do something like this before:


 var getGlobal = function () { if (typeof self !== 'undefined') { return self; } if (typeof window !== 'undefined') { return window; } if (typeof global !== 'undefined') { return global; } throw new Error('unable to locate global object'); }; 

And even this option did not guarantee that everything will work exactly.


globalThis is a common way for all platforms to access the global scope:


 // Обращение к глобальному конструктору массива globalThis.Array(1,2,3) // [1, 2, 3] // Запись собственных данных в глобальную область видимости globalThis.myGLobalSettings = { it_is_cool: true } // Чтение собственных данных из глобальной области видимости globalThis.myGLobalSettings // {it_is_cool: true} 



Dynamic import(dynamic)


https://github.com/tc39/proposal-dynamic-import


browser support

Browser support for dynamic imports


I wanted variables in import strings‽ With dynamic imports, it became possible:


 import(`./language-packs/${navigator.language}.js`) 

Dynamic import is an asynchronous operation. Returns a promise that, after loading a module, returns it to the callback function.


Therefore, modules can be loaded - deferred, when necessary:


 element.addEventListener('click', async () => { // можно использовать await синтаксис для промиса const module = await import(`./events_scripts/supperButtonClickEvent.js`) module.clickEvent() }) 

Syntactically, it looks like a call to the import() function, but is not inherited from Function.prototype , which means it will not be possible to call via call or apply :


 import.call("example this", "argument") // Uncaught SyntaxError: Unexpected identifier 



import.meta - meta-information about the loaded module.


https://github.com/tc39/proposal-import-meta


works in chrome


In the code of the loadable module it became possible to obtain information on it. Now it is only the address where the module was loaded:


 console.log(import.meta); // { url: "file:///home/user/my-module.js" } 



Creating an object using Object.fromEntries()


https://github.com/tc39/proposal-object-from-entries


Analog to _.fromPairs from lodash :


 Object.fromEntries([['key_1', 1], ['key_2', 2]]) // {key_1: 1; key_2: 2} 



Fix method JSON.stringify()


https://github.com/tc39/proposal-well-formed-stringify


Section 8.1 of RFC 8259 requires that JSON text exchanged outside a closed ecosystem is encoded using UTF-8, but JSON.stringify may return strings containing code points that are not represented in UTF-8 (in particular, surrogate code points from U + D800 to U + DFFF)


So the string \uDF06\uD834 after processing JSON.stringify () turns into \\udf06\\ud834 :


 /* Непарные суррогатные единицы будут сериализованы с экранированием последовательностей */ JSON.stringify('\uDF06\uD834') '"\\udf06\\ud834"' JSON.stringify('\uDEAD') '"\\udead"' 

This should not be, and the new specification fixes this. Edge and Chrome have already been updated.




Outdated RegExp features


https://github.com/tc39/proposal-regexp-legacy-features


Specification for obsolete RegExp functions, such as the RegExp.$1 , and RegExp.prototype.compile() method.




Prototype methods for strings .trimStart() and .trimEnd()


https://github.com/tc39/proposal-string-left-right-trim


works in chrome


By analogy with the methods .padStart() and .padEnd() , cut off the whitespace at the beginning and end of the line, respectively:


 const one = " hello and let "; const two = "us begin. "; console.log( one.trimStart() + two.trimEnd() ) // "hello and let us begin." 



.matchAll () is a new prototype string method.


https://github.com/tc39/proposal-string-matchall


works in chrome


It works as a .match() method with the g flag turned on, but returns an iterator:


 const string_for_searh = 'olololo' // Вернёт первое вхождение с дополнительной информацией о нём string_for_searh.match(/o/) // ["o", index: 0, input: "olololo", groups: undefined] //Вернёт массив всех вхождений без дополнительной информации string_for_searh.match(/o/g) // ["o", "o", "o", "o"] // Вернёт итератор string_for_searh.matchAll(/o/) // {_r: /o/g, _s: "olololo"} // Итератор возвращает каждое последующее вхождение с подробной информацией, // как если бы мы использовали .match без глобального флага for(const item of string_for_searh.matchAll(/o/)) { console.log(item) } // ["o", index: 0, input: "olololo", groups: undefined] // ["o", index: 2, input: "olololo", groups: undefined] // ["o", index: 4, input: "olololo", groups: undefined] // ["o", index: 6, input: "olololo", groups: undefined] 

The argument must be a regular expression, otherwise an exception will be thrown:


 'olololo'.matchAll('o') // Uncaught TypeError: o is not a regexp! 



One-dimensional arrays with .flat() and .flatMap()


https://github.com/tc39/proposal-flatMap


works in chrome


The array got prototypes .flat() and .flatMap() , which are generally similar to the implementation in lodash , but still have some differences. Optional argument - sets the maximum depth of the tree:


 const deep_deep_array = [ '≥0 — первый уровень', [ '≥1 — второй уровень', [ '≥2 — третий уровень', [ '≥3 — четвёртый уровень', [ '≥4 — пятый уровень' ] ] ] ] ] // 0 — вернёт массив без изменений deep_deep_array.flat(0) //  ["≥0 — первый уровень", Array(2)] // 1 — глубина по умолчанию deep_deep_array.flat() //  ["первый уровень", "второй уровень", Array(2)] deep_deep_array.flat(2) //  ["первый уровень", "второй уровень", "третий уровень", Array(2)] deep_deep_array.flat(100500) // ["первый уровень", "второй уровень", "третий уровень", "четвёртый уровень", "пятый уровень"] 

.flatMap() equivalent to a sequential call to .map().flat() . The callback function passed to the method must return an array that will become part of a common flat array:


 ['Hello', 'World'].flatMap(word => [...word]) // ["H", "e", "l", "l", "o", "W", "o", "r", "l", "d"] 

Using only .flat() and .map() , the example can be rewritten as:


  ['Hello', 'World'].map(word => [...word]).flat() // ["H", "e", "l", "l", "o", "W", "o", "r", "l", "d"] 

Also note that .flatMap() , unlike .flat() does not have a detour depth setting. So only the first level will be stuck together.






Results


Stage 4 brought more cosmetic changes. Of interest is Stage 3 . Most of the proposals in Chrome have already been implemented, except perhaps Object.fromEntries() , the presence of which is not critical, and we are Object.fromEntries() forward to private properties.






Corrections in the article



If you notice an inaccuracy in the article, a mistake or something to add - you can write me a personal message , but rather you can use the article’s repository https://github.com/KasperGreen/es10 yourself. For an active contribution, I will reward with a yellow magnet-medal with KDPV.


Materials on the topic


English material The current version of the standard Ecma-262


English material Draft of the next version of the standard Ecma-262


ECMAScript


New # private class fields in javascript


Article on Habré Overview of ES7, ES8 and ES9 standards


Shebang


Article on Habré BigInt - long arithmetic in javascript


Article on Habré JavaScript module path


English material Why not private x


Article on Habré ECMAScript Proposal: Array.prototype. {Flat, flatMap}






Alternative KDPV with a yellow magnet from elfafeya.art
Photo by: kasper.green; Yellow Magnet: elfafeya.art & kasper.green



Source: https://habr.com/ru/post/437806/