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()- a prototype method updated # .




    Stage 3 - Pre-release #


    •      #- private all classes, through # ;


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


    •      BigInt()- new primitive, for arbitrary precision numbers # ;


    •      globalThis- new way to access global context # ;


    •      import(dynamic)- dynamic import # ;


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


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


    •      JSON.stringify()- fix method # ;


    •      RegExp- outdated features # ;


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


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


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


    The results of the #




    Five stages


       Stage 0    ↓    Strawman   Basting            idea that can be realized through Babel -plagin .;


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


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


       Stage 3    ↓    Candidate   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 catch


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


    Before  ES 10, the block catchrequired a mandatory argument to collect error information, even if it is not used:


    functionisValidJSON(text) {
      try {
        JSON.parse(text);
        returntrue;
      } catch(unusedVariable) { // переменная не используетсяreturnfalse;
      }
    }


    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 catchwill become 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
    functionisValidJSON(text) {
      try {
        JSON.parse(text);
        returntrue;
      } catch { // без аргументаreturnfalse;
      }
    }

     
     


    Access to the symbolic link description


    https://tc39.github.io/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 return - undefined:


    const without_description_symbol_link = Symbol()
    without_description_symbol_link.description
    // undefinedconst 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 JSON.parse, but this is not true.


    JSON lines can contain unshielded line separators LINE SEPARATOR and paragraphs PARAGRAPH SEPARATOR .U+2028U+2029


    ECMAScript lines up to version 10 are not.


    If you call Edgeeval() with a string "\u2029",
    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.github.io/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;


    • стандартизировать строковое представление встроенных функций и хост-объектов;


    • уточнить требования к представлению на основе «фактических характеристик» объекта;


    • убедиться, что синтаксический анализ строки содержит то же тело функции и список параметров, что и оригинал;


    • для функций, определенных с использованием кода ECMAScript, toString должен возвращать фрагмент исходного текста от начала первого токена до конца последнего токена, соответствующего соответствующей грамматической конструкции;


    • для встроенных функциональных объектов toStringне должны возвращать ничего, кроме NativeFunction;


    • для вызываемых объектов, которые не были определены с использованием кода ECMAScript, toString необходимо вернуть NativeFunction;


    • для функций, создаваемых динамически (конструкторы функции или генератора) toString, должен синтезировать исходный текст;


    • для всех других объектов, toString должен бросить TypeError исключение.



    // Пользовательская функция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:


    <?phpclassAdultContent{
        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';
            }
        }
        privatefunction_getContent(){
            if($this->_contentIsAllowed()) {
                return$this->_content;
            }
            return'Sorry. Content not for you.';
        }
        privatefunction_contentIsAllowed(){
            return$this->_age >= 18;
        }
        function__toString(){
            return$this->content;
        }
    }
    echo"<pre>";
    echo strval(new AdultContent(10));
    //  (age: 10) →  Sorry. Content not for youecho 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:


    exportdefaultclassAdultContent{
      // Приватные атрибуты класса
      #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)){
            returnthis.#adult_content
        }
        else {
            return'Sorry. Content not for you.'
        }
      }
      // Приватный метод
      #setAge(age) {
          this.#age = age
      }
      toString () {
        returnthis.#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 outSyntaxError: Invalid or&nbsp;unexpected token
     
     


    Big numbers with BigInt


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


    browser support

    Поддержка браузерами примитива 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( "9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999" )
    \\ 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999n

    This is a new primitive type:


    typeof123;
    // → 'number'typeof123n;
    // → 'bigint'

    It can be compared with ordinary numbers:


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

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


    20000000000000n/20n
    // 1000000000000n20000000000000n/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 (typeofwindow !== 'undefined') { returnwindow; }
        if (typeof global !== 'undefined') { return global; }
        thrownewError('unable to locate global object');
    };

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


    globalThis - 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

    Поддержка браузерами динамических импортов


    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 синтаксис для промисаconstmodule = awaitimport(`./events_scripts/supperButtonClickEvent.js`)
        module.clickEvent()
    })

    Syntactically, it looks like a function call import(), but is not inherited from  Function.prototype, which means it cannot be called via callor 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 with a method Object.fromEntries()


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


    Analog _.fromPairsfrom 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\uD834after 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 deprecated features the RegExp , kind RegExp.$1and  RegExp.prototype.compile() method.


     
     


    Prototype string methods .trimStart()and.trimEnd()


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


    works in chrome


    By analogy with the .padStart()and methods .padEnd(), cut 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


    Works as a method .match()with the flag turned on g, 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 .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(), an example can be rewritten as:


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

    You also need to take into account that, .flatMap()unlike in .flat()no settings for the depth of the traversal. 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, with the exception of perhaps Object.fromEntries(), the presence of which is not critical, and we are waiting for 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 materialThe current version of the standard Ecma-262


    English materialDraft 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 materialWhy 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

    Only registered users can participate in the survey. Sign in , please.

    The most expected feature:


    Also popular now: