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, не считая расширения уже имеющихся объектов.
                    • –2
                      Синтаксический сахар классов конечно впечатляет, но лучше бы сделали нормальное множественное наследование с интерфейсами и статическими методами, а не а-ля typescript
                      • 0
                        Множественное то зачем?
                        • 0
                          Хотел написать, многоуровневое наследование
                          • –1
                            Многоуровневое наследование — это не то, что можно сделать в прототипно-ориентированном языке.
                      • –1
                        В ES6 появляются «настоящие» классы:

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

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

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