Ecmascript 6 — что можно использовать уже сейчас



    Примечание: статья расчитана в основном на не-Javascript программистов — иногда я буду вдаваться в объяснения достаточно основных вещей, но надеюсь будет полезна и тем, кто просто не успел ознакомиться с большинством нововведений ES6.

    Как известно, стандарт Ecmascript 6 собираются опубликовать в июне 2015. Но так как многое уже имплементировано в современный браузерах, почему-бы не начать использовать это прямо сейчас?
    Поскольку jsFiddle и аналоги ES6 не поддерживают, буду использовать es6fiddle для примеров. К сожалению, не все в нем можно показать из-за багов. При отсутствии ссылок на es6fiddle рекомендую копировать сниппеты кода в консоль современного браузера и выполнять их — для наглядности. Рекомендую крайний стабильный Firefox (версия 33 на момент написания статьи) — там все работает «из коробки».



    Так как изменений очень много, опишу субъективно самые важные.

    let и scope — переменные с блочной областью видимости (block scope)


    Одна из самых раздражающих/непонятных для начинающих JS-программистов вещей это область видимости переменных в JS. Пример:
    for(var i=0; i<10; i++){ }
    console.log(i);
    


    В большинстве языков подобный код выкинет ошибку что i не дефинирована, но в JS в консоль выведется число «10». Причина в том, что в JS используется hoisting — то есть декларации всех используемых переменных переносятся в начало функции (и соответственно i доступна вне блока for). Именно поэтому советуют декларировать все переменные в начале функции — все равно они будут туда перенесены при выполнении кода.
    В ES6 можно написать:
    for(let j=0; j<10; j++){ }
    console.log(j);
    


    В этом коде область видимости j ограничена блоком for. Больше нет смысла декларировать переменные в начале функции!

    Про const говорить особо нечего — это декларация константы, при повторном присвоении значения выбрасывает TypeError (в strict mode).

    Пример:
    function setConst(){
     "use strict";
      
      const xx = 10;
      xx = 11;
      
      console.log(xx);
    }
    setConst();
    


    Ссылок на примеры нет, так как на данный момент block scope в es6fiddle не поддерживается. Ждем фиксов.

    Arrow functions


    Если вы использовали лямбды в C#, вы сразу узнаете синтаксис (это не случайность — он был взят именно оттуда).
    Как писали раньше:
    function SomeClass() {
      var self = this; //самый простой способ сохранить контекст нужного объекта
      self.iter = 0;
    
      setInterval(function() {
        self.iter++;
        console.log('current iteration: ' + self.iter);
      }, 1000);
    }
    
    var sc = new SomeClass();
    


    В ES6 можно гораздо проще:
    function SomeClass() {
      this.iter = 0;
    
      setInterval(() => {
        this.iter++;
        console.log('current iteration: ' + this.iter);
      }, 1000);
    }
    
    var sc = new SomeClass();
    

    es6fiddle

    Как видно, arrow function не создает своего контекста, так что this будет указывать на контекст функции, в которой вызван код.

    Пример с параметрами:
    let numList = [1,2,3];
    let doubleNumList = numList.map(n => n*2);
    console.log(doubleNumList);
    

    es6fiddle

    Сравните, насколько более громоздкий старый метод:
    let numList = [1,2,3];
    let doubleNumList = numList.map(function(n){ return n*2; });
    console.log(doubleNumList);
    


    Классы


    Старый способ эмулировать классы в JS выглядит как-то так:
    function Vehicle(topSpeed){
     this.topSpeed = topSpeed;
     
        this.printTopSpeed = function(){
         console.log('Top speed:'+this.topSpeed+' km/h');   
        }
    }
    
    var myVehicle = new Vehicle(50);
    myVehicle.printTopSpeed();
    

    jsFiddle

    «Традиционное» наследование как в Java/C# можно также эмулировать через протопипную модель (код приводить не буду, чтобы не раздувать статью).
    В ES6 появляются «настоящие» классы:
    class Vehicle {
      constructor(topSpeed){
       this.topSpeed = topSpeed;
      }
      
      printTopSpeed(){
        console.log('Top speed:'+this.topSpeed+' km/h');
      }
    }
    
    class Bicycle extends Vehicle {
      constructor(topSpeed, wheelSize, bicycleType, producedBy){
       super(topSpeed);
       this.wheelSize = wheelSize;
       this.bicycleType = bicycleType;
       this.producedBy = producedBy;
      }
      
      static wheelCount(){ return 2; }
      
      get bikeInfo(){
      	return this.producedBy + ' ' + this.bicycleType + ' bike';
      }
      
      printBicycleType(){
        console.log('Type:'+this.bicycleType+' bike');
      }
    }
    
    var myBike = new Bicycle(40,622,'road','Trek');
    
    myBike.printTopSpeed();
    myBike.printBicycleType();
    
    console.log('Bicycles have '+Bicycle.wheelCount()+' wheels');
    
    console.log(myBike.bikeInfo);
    

    es6fiddle

    Думаю, что код выше в комментариях не нуждается. Как видно, в классах можно дефинировать геттеры и статические методы (но не поля), синтаксис интуитивно понятен. Стоит отметить, что тело класса (class body) всегда интерпретируется в strict mode. Ну и конечно никто не заставляет оголтелых адептов прототипного наследования менять свои привычки — классы это вообщем-то синтаксический сахар поверх старой модели.

    Destructuring assignment


    Напоследок, небольшая но интересная фича, которая облегчает присвоение значений. Пример:
    function getFirstPrimeNumbers(){
     return [2,3,5]; 
    }
    
    var [firstPrime, secondPrime, thirdPrime] = getFirstPrimeNumbers();
    
    console.log(thirdPrime); //5
    
    //обмен значений (value swap)
    var [x,y] = [1,2];
    console.log('x:'+x, 'y:'+y); //x:1 y:2
    [x,y] = [y,x];
    console.log('x:'+x, 'y:'+y); //x:2 y:1
    

    es6fiddle

    Теперь можно обменивать значения без временной переменной, что сокращает код.

    Немного более сложный пример:
    var myBook = {
      title: "Surely You're Joking, Mr. Feynman!",
      author:{
        firstName: 'Richard',
        lastName: 'Feynman',
        yearBorn: 1918
      }
    };
    
    function getTitleAndAuthorsLastName({ title, author: { lastName } }){
      return 'title: '+ title + ' last name:'+lastName;
    }
    
    console.log(getTitleAndAuthorsLastName(myBook));
    

    es6fiddle

    Как использовать?


    Предвижу возмущение — как видно из все той же таблицы, кое-где ES6 сразу не работает. Хром, к примеру, не поддерживает большинство вещей из списка, если не включен флаг Enable Experimental JavaScript. Выход — использовать Traceur, компилятор из ES 6 в ES 5.
    Именно он используется в es6fiddle, кстати. На странице в гитхабе есть инструкция по использованию. Вкратце для браузера:

    1. Подключаем 2 JS файла:
        <script src="https://google.github.io/traceur-compiler/bin/traceur.js"></script>
        <script src="https://google.github.io/traceur-compiler/src/bootstrap.js"></script> 
    

    2. Добавляем для поддержки эксперементальных фич ES6:
        <script>
          traceur.options.experimental = true;
        </script>
    

    3. Пишем код в script тегах с типом module:
    <script type="module">
    let a = 10;
    </script>
    


    Заключение


    Надеюсь, что данная статья смогла показать, что Javascript на данный момент развился в достаточно серьезный язык программирования (хоть и с местами странным поведением). Это не значит что надо выкидывать любимые Java/C#/C++ на помойку, это значит что овладеть джаваскриптом стало еще легче, за счет удобного синтаксиса и более стабильного поведения.
    Поделиться публикацией

    Похожие публикации

    Комментарии 39
      0
      Было же уже? И некоторые вещи (вроде генераторов и arrow-функций) описывались даже более подробно
        0
        Да, согласен — частично пересекается. Но:
        1. Той статье больше года.
        2. Я старался показать нововведения с точки зрения Java/C# программиста, та статья все-же для JS-программистов имхо.
          0
          Да, но разве вы исследуете какие-то новые API с момента прошлогодней публикации?
          Объясните мне, в чём разница взгляда на спецификацию со стороны JavaScript-программиста и Java/C# программиста? Может стоит написать ещё несколько обзоров с точки зрения Python / Ruby / Haskell программистов?
            +4
            Объясните мне, в чём разница взгляда на спецификацию со стороны JavaScript-программиста и Java/C# программиста?

            Ок, поясняю. Java-программист, который на JS не писал ничего сложнее ajax-вызова через jQuery скорее всего плохо знаком с прототипным наследованием. Теперь этот человек может начать писать более сложную логику на JS, не выходя из своей зоны комфорта и используя привычные классы. Может я не сделал на этом акцент — тогда моя вина. Ну и как-бы в 2014 ES6 API уже можно начинать использовать. Не просто там рассуждать как хорошо будет, а брать и писать. Каким образом — я тоже пояснил.

            Может стоит написать ещё несколько обзоров с точки зрения Python / Ruby / Haskell программистов?

            Да, я вот почитал-бы взгляд на JS со стороны Haskell-прогера. Потому что в хаскелле совершенно другие конструкции и соответственно другие привычки у человека. Напишете?
              +1
              Спасибо за развернутый ответ! Хоть вопрос и выглядит несколько саркастичным (за что я уже поплатился), он таковым не является. Мне правда интересно, как видят язык программисты из других областей, <имхо>но я всё же считаю излишним описывать каждую спецификацию со стороны программистов других областей (представьте, какой каламбур будет, если JS программисты будут описывать новые спецификации C++ и т.п.)</имхо>.

              По поводу взгляд на JS со стороны Haskell-программиста: к сожалению, не напишу из-за слабого знания последнего. Однако, если вам интересно почитать про функциональное программирование на JS, то на хабре уже есть несколько статей на эту тему.

              Есть у ECMA-6 и Java/C# что-то общее помимо классов (которые являются синтаксическим сахаром поверх прототипного наследования) и лямбд? Вы пишете про сравнение с точки зрения Java/C# программиста, а упоминание данных ЯП я вижу только в двух местах.

              Хром, к примеру, не поддерживает большинство вещей из списка, если не включен флаг Enable Experimental JavaScript. Выход — использовать Traceur, компилятор из ES 6 в ES 5.

              Ситуация за год не изменилась.
              Ну и как-бы в 2014 ES6 API уже можно начинать использовать
              Это мнение автора или чем-то обоснованная позиция? Так как если вы посмотрите на traceur и его дату создания, то он существовал ещё с 2011 года (хоть и поддерживал далеко не всё), что позволяло ещё в тех годах использовать вышеописанные в статье фичи новой спецификации.

              <имхо>Мне нравится статья и как она написана (за мелкими исключениями), но либо вы опоздали с её публикацией (на год), либо сделали не на то акцент (потому что выглядит как дополнение к предыдущей статье). И извините за прямолинейность и не сочтите за оскорбление.</имхо>
                0
                Ок, не буду спорить, возможно я слабо развил тему похожести конструкций в ES6 и Java/C#, а так-же упустил возможность описать некоторые нововведения, которые не были описаны в той статье. Думаю, на этом можно прекратить спор.

                По поводу вашего первого имхо скажу свое (имхо): JS сейчас в тренде, и многие программисты на других языках могут захотеть хотя-бы даже просто посмотреть «а-все-ли-так-печально-как-10-лет-назад». Однако я не видел много туториалов по JS, где использовали-бы классы, и возможность использовать ES6 to ES5 компилятор не лежит на поверхности.
        +18
        <offtopic>Ну ладно «декларировать», черт с ним. Но «дефинировать» — это уже перебор, по-моему.</offtopic>
          –8
          Слово «дефиниция» в русском существует, нравится вам это или нет.
            +9
            И глагол «дефинировать»? Или, может быть, «дефиницировать»?
              –13
              Да, если есть существительное, значит от него можно образовать глагол. Вот, читайте. Если вы не знаете этого слова, это не значит что его нет. В следующий раз можете погуглить, прежде чем бороться за «русскость».
                +18
                А зачем вы выдумали какого-то словесного урода, если есть общепринятый и всем понятный термин «определять»?
                  –11
                  Если вам непонятен термин «дефиниция», то у меня для вас плохие новости.
                    +7
                    «дефиниция»

                    у меня нехорошие ассоциации…
                  +11
                  Есть более привычный/распространенный вариант «объявить».
                    –12
                    Нераспространенный — согласен. «Словесный урод» и т.п. — нет.
                    +9
                    Что касается частоты использования
                    дефинировать переменную — 1 210 результатов
                    определить переменную — 667 000 результатов
                    объявить переменную — 164 000 результатов
              +1
              Есть альтернатива Traceur — 6to5, может работать без runtime скрипта и генерирует более человеческий код. Из всех трансляторов которые пробовал, 6to5 показал себя самым адекватным.
                0
                Спасибо, попробую.
                  0
                  Да только он один из самых медленных при компиляции.
                  +1
                  Спасибо, вспомнил некоторые забытые вещи :)

                  Пара комментариев:

                  «Традиционное» наследование как в Java/C# можно так-же эмулировать через протопипную модель (код приводить не буду, чтобы не раздувать статью).

                  habrahabr.ru/post/132698/#comment_4404597

                  Теперь можно обменивать значения без временной переменной, что сокращает код.

                  Можно ещё так:
                  var x = 1, y = 2
                  x = [y, y=x][0]
                  
                    0
                    можно подумать, что это наследование — не просто сахар поверх.

                    Доказательство(пишу по памяти, могу где-то немного накосячить):

                    class Horse{
                    constructor(){this._speed = 5;}
                    get speed(){return this._speed}
                    }
                    class HorseWithTransmission extends Horse{
                    set speed(val){this._speed = val}
                    }
                    
                    new HorseWithTransmission().speed //=> undefined
                    

                    Неожиданно, правда? А все потому что это обертка поверх Object.create
                    +5
                    class Bicycle extends Vehicle {
                    Сдались.
                      +2
                      Вы не упомянули про
                      var name = 'nkt';
                      console.log(`Hello, ${name}!`); // Hello nkt
                      

                      Очень удобная фича, учитывая отсутствие sprintf по дефолту.

                      Ну и в догонку
                      var name = 'nkt';
                      var options = {name}; // {name: name}
                      
                        +2
                        Так тут почти ни о чём не упомянули. Еще, на вскидку: модульная система, генераторы, протокол итераторов, for-of, абстракции массивов / генераторов, аргументы функции по умолчанию, rest / spread, динамические ключи в литерале объекта, оптимизация хвостовой рекурсии, стандартная библиотека: Symbol, Proxy, Promise, Map, Set, WeakMap, WeakSet, не считая расширения уже имеющихся объектов.
                          0
                          Ну во-первых не ставил я цель написать обо всем, извините. Об этом я предупреждаю:

                          Так как изменений очень много, опишу субъективно самые важные.


                          А во вторых часть из упомянутого вами сейчас не работает нигде, включая компиляторы в ES5 (типа оптимизации хвостовой рекурсии).
                      –2
                      Синтаксический сахар классов конечно впечатляет, но лучше бы сделали нормальное множественное наследование с интерфейсами и статическими методами, а не а-ля typescript
                        0
                        Множественное то зачем?
                          0
                          Хотел написать, многоуровневое наследование
                            –1
                            Многоуровневое наследование — это не то, что можно сделать в прототипно-ориентированном языке.
                        –1
                        В ES6 появляются «настоящие» классы:

                        Напомнило это.
                        Печально все это.
                          –1
                          Не появятся, я выше не написал — это просто сахар. Можно не волноваться, будет адов стыд и глюкало :)
                          –1
                          Не понимаю, зачем они это делают. Я имею в виду, вводят новые фичи в яваскрипт.
                          Я яваскриптом уже несколько лет не занимался, но даже я с пяток языков, компилирующихся в яваскрипт, вспомню.
                          Наоборот надо упрощать. А еще лучше VM для браузеров стандартизовать.
                          А кто хочет, тот скомпилирует в него всё, что угодно.
                          Может такое уже есть, или может ведутся работы, кто знает?
                            0
                            Лучше-бы иметь общий байткод, конечно. Уверен, все к этому прийдет, в итоге.
                            0
                            let отдает бейсиком
                              0
                              Для кого бейсик, для кого F# ;)

                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                            Самое читаемое