Примечание: статья расчитана в основном на не-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();
Как видно, arrow function не создает своего контекста, так что this будет указывать на контекст функции, в которой вызван код.
Пример с параметрами:
let numList = [1,2,3];
let doubleNumList = numList.map(n => n*2);
console.log(doubleNumList);
Сравните, насколько более громоздкий старый метод:
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();
«Традиционное» наследование как в 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);
Думаю, что код выше в комментариях не нуждается. Как видно, в классах можно дефинировать геттеры и статические методы (но не поля), синтаксис интуитивно понятен. Стоит отметить, что тело класса (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
Теперь можно обменивать значения без временной переменной, что сокращает код.
Немного более сложный пример:
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));
Как использовать?
Предвижу возмущение — как видно из все той-же таблицы, кое-где 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++ на помойку, это значит что овладеть джаваскриптом стало еще легче, за счет удобного синтаксиса и более стабильного поведения.
Автор: Ununtrium