От переводчика:
Предлагаю вашему вниманию перевод краткого (действительно краткого) руководства по ES6. В нём можно ознакомиться с основными понятиями стандарта.
Оригинальный текст в некоторых случаях был дополнен или заменён на более подходящий источник. Например, часть определения ключевого слова const является переводом документации с MDN.
Чтобы лучше разобраться в некоторых концепциях (для выполнения качественного перевода) использовалось описание стандарта на сайте MDN, руководство "You Don't Know JS: ES6 & Beyond" и учебник Ильи Кантора.


Перевод выложил на Гитхаб: https://github.com/etnolover/ES6-for-humans-translation. В случае нахождения ошибок пишите, исправлю.
Ссылка на оригинальный текст: https://github.com/metagrover/ES6-for-humans


Update 22.07.2016: добавил опрос про const


Содержание






1. let, const и блочная область видимости


Ключевое слово let позволяет объявлять переменные с ограниченной областью видимости — только для блока {...}, в котором происходит объявление. Это называется блочной областью видимости. Вместо ключевого слова var, которое обеспечивает область видимости внутри функции, стандарт ES6 рекомендует использовать let.


var a = 2;
{
    let a = 3;
    console.log(a); // 3
}
console.log(a); // 2

Другой формой объявления переменной с блочной областью видимости является ключевое слово const. Оно предназначено для объявления переменных (констант), значения которых доступны только для чтения. Это означает не то, что значение константы неизменно, а то, что идентификатор переменной не может быть переприсвоен.
Вот простой пример:


{
    const ARR = [5, 6];
    ARR.push(7);
    console.log(ARR); // [5,6,7]
    ARR = 10; // TypeError
    ARR[0] = 3; // значение можно менять
    console.log(ARR); // [3,6,7]
}

О чём стоит помнить:


  • Когда дело касается поднятия переменных (hoisting) let и const, их поведение отличается от традиционного поведения var и function. И let и const не существуют до своего объявления (от переводчика: для подробностей автор оригинального руководства отсылает к статье Temporal Dead Zone)
  • Областью видимости let и const является ближайший блок.
  • При использовании const рекомендуется использовать ПРОПИСНЫЕ_БУКВЫ.
  • В const одновременно с объявлением переменной должно быть присвоено значение.



2. Стрелочные функции


Стрелочные функции представляют собой сокращённую запись функций в ES6. Стрелочная функция состоит из списка параметров ( ... ), за которым следует знак => и тело функции.


// Классическое функциональное выражение
let addition = function(a, b) {
    return a + b;
};

// Стрелочная функция
let addition = (a, b) => a + b;

Заметим, что в примере выше, тело функции представляет собой краткую запись, в которой не требуется явного указания на то, что мы хотим вернуть результат.


А вот пример с использованием блока из фигурных скобок:


let arr = ['apple', 'banana', 'orange'];

let breakfast = arr.map(fruit => {
    return fruit + 's';
});

console.log(breakfast); // ['apples', 'bananas', 'oranges']

Это ещё не всё!...


Стрелочные функции не просто делают код короче. Они тесно связаны с ключевым словом this и привязкой контекста.


Поведение стрелочных функций с ключевым словом this отличается от поведения обычных функций с this. Каждая функция в JavaScript определяет свой собственный контекст this, но внутри стрелочных функций значение this то же самое, что и снаружи (стрелочные функции не имеют своего this). Посмотрим на следующий код:


function Person() {
    // Конструктор Person() определяет `this` как экземпляр самого себя.
    this.age = 0;

    setInterval(function growUp() {
        // Без использования `use strict`, функция growUp() определяет `this`
        // как глобальный объект, который отличается от `this`,
        // определённого конструктором Person().
        this.age++;
    }, 1000);
}
var p = new Person();

В ECMAScript 3/5 это поведение стало возможным изменить, присвоив значение this другой переменной.


function Person() {
    var self = this;
    self.age = 0;

    setInterval(function growUp() {
        // Коллбэк относится к переменной `self`,
        // значением которой является ожидаемый объект.
        self.age++;
    }, 1000);
}

Как сказано выше, внутри стрелочных функций значение this то же самое, что и снаружи, поэтому следующий код работает так, как от него и ожидается:


function Person() {
    this.age = 0;

    setInterval(() => {
        this.age++; // `this` относится к объекту person
    }, 1000);
}

var p = new Person();

Узнать больше о 'Лексическом this' в стрелочных функциях на сайте MDN




3. Параметры по умолчанию


ES6 позволяет установить параметры по умолчанию при объявлении функции. Вот простой пример:


let getFinalPrice = (price, tax = 0.7) => price + price * tax;
getFinalPrice(500); // 850, так как значение tax не задано

getFinalPrice(500, 0.2); // 600, значение tax по-умолчанию заменяется на 0.2



4. Spread / Rest оператор


... оператор называют как spread или rest, в зависимости от того, как и где он используется.


При использовании в любом итерируемом объекте (iterable), данный оператор "разбивает" ("spread") его на индивидуальные элементы:


function foo(x, y, z) {
    console.log(x, y, z);
}

let arr = [1, 2, 3];
foo(...arr); // 1 2 3

Другим распространённым использованием оператора ... является объединение набора значений в один массив. В данном случае оператор работает как "rest" (от переводчика: не нашёл подходящего перевода на русский язык, из примера ниже всё станет ясно)


function foo(...args) {
    console.log(args);
}
foo(1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]



5. Расширение возможностей литералов объекта


ES6 позволяет объявить литералы объекта с помощью короткого синтаксиса для инициализации свойств из переменных и определения функциональных методов. Также, стандарт обеспечивает возможность вычисления свойств непосредственно в литерале объекта.


function getCar(make, model, value) {
    return {
        // с синтаксисом короткой записи можно
        // пропускать значение свойства, если оно
        // совпадает с именем переменной, значение
        // которой мы хотим использовать
        make,  // аналогично make: make
        model, // аналогично model: model
        value, // аналогично value: value

        // вычисляемые свойства теперь работают в
        // литералах объекта
        ['make' + make]: true,

        // Короткая запись метода объекта пропускает
        // ключевое слово `function` и двоеточие. Вместо
        // "depreciate: function() {}" можно написать:
        depreciate() {
            this.value -= 2500;
        }
    };
}

let car = getCar('Kia', 'Sorento', 40000);
console.log(car);
// {
//     make: 'Kia',
//     model:'Sorento',
//     value: 40000,
//     makeKia: true,
//     depreciate: function()
// }



6. Восьмеричный и двоичный литералы


В ES6 появилась новая поддержка для восьмеричных и двоичных литералов.
Добавление к началу числа 0o или 0O преобразует его в восьмеричную систему счисления (аналогично, 0b или 0B преобразует в двоичную систему счисления). Посмотрим на следующий код:


let oValue = 0o10;
console.log(oValue); // 8

let bValue = 0b10;
console.log(bValue); // 2



7. Деструктуризация массивов и объектов


Деструктуризация помогает избежать использования вспомогательных переменных при взаимодействии с объектами и массивами.


function foo() {
    return [1, 2, 3];
}
let arr = foo(); // [1,2,3]

let [a, b, c] = foo();
console.log(a, b, c); // 1 2 3

function bar() {
    return {
        x: 4,
        y: 5,
        z: 6
    };
}
let { x: a, y: b, z: c } = bar();
console.log(a, b, c); // 4 5 6



8. Ключевое слово super для объектов


ES6 позволяет использовать метод super в (безклассовых) объектах с прототипами. Вот простой пример:


var parent = {
    foo() {
        console.log("Привет от Родителя!");
    }
}

var child = {
    foo() {
        super.foo();
        console.log("Привет от Ребёнка!");
    }
}

Object.setPrototypeOf(child, parent);
child.foo(); // Привет от Родителя!
             // Привет от Ребёнка!



9. Строковые шаблоны и разделители


ES6 предоставяляет более простой способ вставки значения переменной или результата выражения (т.н. "интерполяцию"), которые рассчитываются автоматически.


  • ${ ... } используется для вычисления значения переменной/выражения.
  • `` Обратные кавычки используются как разделитель для таких случаев.

let user = 'Кевин';
console.log(`Привет, ${user}!`); // Привет, Кевин!



10. for...of против for...in


  • for...of используется для перебора в цикле итерируемых объектов, например, массивов.

let nicknames = ['di', 'boo', 'punkeye'];
nicknames.size = 3;
for (let nickname of nicknames) {
    console.log(nickname);
}
// di
// boo
// punkeye

  • for...in используется для перебора в цикле всех доступных для перебора (enumerable) свойств объекта.

let nicknames = ['di', 'boo', 'punkeye'];
nicknames.size = 3;
for (let nickname in nicknames) {
    console.log(nickname);
}
// 0
// 1
// 2
// size



11. Map и WeakMap


ES6 представляет новые структуры данных — Map и WeakMap. На самом деле, мы используем "Map" в JavaScript всё время. Каждый объект можно представить как частный случай Map.


Классический объект состоит из ключей (всегда в строковом виде) и значений, тогда как в Map для ключа и значения можно использовать любое значение (и объекты, и примитивы). Посмотрим на этот код:


var myMap = new Map();

var keyString = "строка",
    keyObj = {},
    keyFunc = function() {};

// устанавливаем значения
myMap.set(keyString, "значение, связанное со 'строка'");
myMap.set(keyObj, "значение, связанное с keyObj");
myMap.set(keyFunc, "значение, связанное с keyFunc");

myMap.size; // 3

// получаем значения
myMap.get(keyString);    // "значение, связанное со 'строка'"
myMap.get(keyObj);       // "значение, связанное с keyObj"
myMap.get(keyFunc);      // "значение, связанное с keyFunc"

WeakMap


WeakMap это Map, в котором ключи обладают неустойчивыми связями, что позволяет не мешать сборщику мусора удалять элементы WeakMap. Это означает, что можно не беспокоиться об утечках памяти.


Стоить отметить, что в WeakMap, в отличие от Map, каждый ключ должен быть объектом.


Для WeakMap есть только четыре метода: delete(ключ), has(ключ), get(ключ) и set(ключ, значение).


let w = new WeakMap();
w.set('a', 'b');
// Uncaught TypeError: Invalid value used as weak map key

var o1 = {},
    o2 = function(){},
    o3 = window;

w.set(o1, 37);
w.set(o2, "azerty");
w.set(o3, undefined);

w.get(o3); // undefined, потому что это заданное значение

w.has(o1); // true
w.delete(o1);
w.has(o1); // false



12. Set и WeakSet


Объекты Set это коллекции уникальных значений. Дублированные значения игнорируются, т.к. коллекция должна содержать только уникальные значения. Значения могут быть примитивами или ссылками на объекты.


let mySet = new Set([1, 1, 2, 2, 3, 3]);
mySet.size; // 3
mySet.has(1); // true
mySet.add('строки');
mySet.add({ a: 1, b:2 });

Вы можете перебирать Set в цикле с помощью forEach или for...of. Перебор происходит в том же порядке, что и вставка.


mySet.forEach((item) => {
    console.log(item);
    // 1
    // 2
    // 3
    // 'строки'
    // Object { a: 1, b: 2 }
});

for (let value of mySet) {
    console.log(value);
    // 1
    // 2
    // 3
    // 'строки'
    // Object { a: 1, b: 2 }
}

У Set также есть методы delete() и clear().


WeakSet


Аналогично WeakMap, объект WeakSet позволяет хранить объекты с неустойчивыми связями в коллекции. Объект в WeakSet уникален.


var ws = new WeakSet();
var obj = {};
var foo = {};

ws.add(window);
ws.add(obj);

ws.has(window); // true
ws.has(foo);    // false, foo не был добавлен к коллекции

ws.delete(window); // удаляет window из коллекции
ws.has(window);    // false, window был удалён



13. Классы в ES6


В ES6 представили новый синтаксис для классов. Здесь стоит отметить, что класс ES6 не представляет собой новую объектно-ориентированную модель наследования. Это просто синтаксический сахар для существующего в JavaScript прототипного наследования.


Класс в ES6 представляет собой просто новый синтаксис для работы с прототипами и функциями-конструкторами, которые мы привыкли использовать в ES5.


Функции, записанные с помощью ключевого слова static, используются для объявления статических свойств класса.


class Task {
    constructor() {
        console.log("Создан экземпляр task!");
    }

    showId() {
        console.log(23);
    }

    static loadAll() {
        console.log("Загружаем все tasks...");
    }
}

console.log(typeof Task); // function
let task = new Task(); // "Создан экземпляр task!"
task.showId(); // 23
Task.loadAll(); // "Загружаем все tasks..."

extends и super в классах


Посмотрим на следующий код:


class Car {
    constructor() {
        console.log("Создаём новый автомобиль");
    }
}

class Porsche extends Car {
    constructor() {
        super();
        console.log("Создаём Porsche");
    }
}

let c = new Porsche();
// Создаём новый автомобиль
// Создаём Porsche

В ES6 ключевое слово extends позволяет классу-потомку наследовать от родительского класса. Важно отметить, что конструктор класса-потомка должен вызывать super().


Также, в классе-потомке можно вызвать метод родительского класса с помощью super.имяМетодаРодителя().


Узнать больше о классах на сайте MDN


О чём стоит помнить:


  • Объявления классов не поднимаются наверх (not hoisted). Сначала нужно объявить класс и только после этого использовать его, иначе будет ошибка ReferenceError.
  • Нет необходимости использовать ключевое слово function во время задания функций внутри определения класса.



14. Тип данных Symbol


Symbol это уникальный и неизменяемый тип данных, представленный в ES6. Целью Symbol является создание уникального идентификатора, к которому нельзя получить доступ.


Вот как можно создать Symbol:


var sym = Symbol("опциональное описание");
console.log(typeof sym); // symbol

Заметим, что использовать new вместе с Symbol(…) нельзя.


Если Symbol используется как свойство/ключ объекта, он сохраняется таким специальным образом, что свойство не будет показано при нормальном перечислении свойств объекта.


var o = {
    val: 10,
    [Symbol("случайный")]: "Я - символ",
};

console.log(Object.getOwnPropertyNames(o)); // val

Чтобы извлечь символьные свойства объекта, нужно использовать Object.getOwnPropertySymbols(o)




15. Итераторы


Итератор обращается к элементам коллекции по одному, в то же время сохраняя память о своей текущей позиции в этой коллекции. У итератора есть метод next(), который возвращает следующий элемент в последовательности. Этот метод возвращает объект с двумя свойствами: done (окончен ли перебор) и value (значение).


В ES6 есть метод Symbol.iterator, который определяет итератор для объекта по-умолчанию. При каждой необходимости перебора в цикле для объекта (например, в начале цикла for..of), его метод итератора вызывается без аргументов, и возвращённый итератор используется для того, чтобы получить значения для перебора.


Посмотрим на массив, который является перебираемым (iterable), и на итератор, который есть у массива для обработки его значений:


var arr = [11,12,13];
var itr = arr[Symbol.iterator]();

itr.next(); // { value: 11, done: false }
itr.next(); // { value: 12, done: false }
itr.next(); // { value: 13, done: false }

itr.next(); // { value: undefined, done: true }

Заметим, что можно написать собственный итератор через определение obj[Symbol.iterator]() с описанием объекта.


Подробнее про итераторы:
На сайте MDN




16. Генераторы


Функции-генераторы представляют собой новую особенность ES6, которая позволяет функции создавать много значений в течение некоторого периода времени, возвращая объект (называемый генератором), который может быть итерирован для выброса значений из функции по одному за раз.


Функция-генератор возвращает итерируемый объект при своём вызове.
Функция-генератор записывается с помощью знака * после ключевого слова function, а в теле функции должно присутствовать ключевое слово yield.


function *infiniteNumbers() {
    var n = 1;
    while (true) {
        yield n++;
    }
}

var numbers = infiniteNumbers(); // возвращает перебираемый объект

numbers.next(); // { value: 1, done: false }
numbers.next(); // { value: 2, done: false }
numbers.next(); // { value: 3, done: false }

Каждый раз при вызове yield возвращённое значение становится следующим значением в последовательности.


Также заметим, что генераторы вычисляют свои возвращённые значения по запросу, что позволяет им эффективно представлять последовательности, затратные с точки зрения вычислений, или даже бесконечные последовательности.




17. Промисы


В ES6 появилась встроенная поддержка промисов. Промис это объект, который ждёт выполнения асинхронной операции, после которого (т.е. после выполнения) промис принимает одно из двух состояний: fulfilled (resolved, успешное выполнение) или rejected (выполнено с ошибкой).


Стандартным способом создания промиса является конструктор new Promise(), который принимает обработчик с двумя функциями как параметрами. Первый обработчик (обычно именуемый resolve) представляет собой функцию для вызова вместе с будущим значением, когда оно будет готово; второй обработчик (обычно именуемый reject) является функцией, которая вызывается для отказа от выполнения промиса, если он не может определить будущее значение.


var p = new Promise(function(resolve, reject) {  
    if (/* условие */) {
        resolve(/* значение */);  // fulfilled successfully (успешный результат)
    } else {
        reject(/* reason */);  // rejected (ошибка)
    }
});

Каждый промис обладает методом then, в котором есть два коллбэка. Первый коллбэк вызывается, если промис успешно выполнен (resolved), тогда как второй коллбэк вызывается, если промис выполнен с ошибкой (rejected).


p.then((val) => console.log("Промис успешно выполнен", val),
       (err) => console.log("Промис выполнен с ошибкой", err));

При возвращении значения от then коллбэки передадут значение следующему коллбэку then.


var hello = new Promise(function(resolve, reject) {  
    resolve("Привет");
});

hello.then((str) => `${str} Мир`)
     .then((str) => `${str}!`)
     .then((str) => console.log(str)) // Привет Мир!

При возвращении промиса, успешно обработанное значение промиса пройдёт к следующему коллбэку, для того, чтобы эффективно соединить их вместе.
Эта простая техника помогает избежать ада с коллбэками ("callback hell").


var p = new Promise(function(resolve, reject) {  
    resolve(1);
});

var eventuallyAdd1 = (val) => {
    return new Promise(function(resolve, reject){
        resolve(val + 1);
    });
}

p.then(eventuallyAdd1)
 .then(eventuallyAdd1)
 .then((val) => console.log(val)) // 3

При использовании переменных типа `const`:

Проголосовало 142 человека. Воздержалось 16 человек.

Оставить или удалить пункт «Рекомендуется использовать ПРОПИСНЫЕ_БУКВЫ»?

Проголосовал 121 человек. Воздержалось 15 человек.

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

Поделиться с друзьями
-->

Комментарии (87)


  1. titulusdesiderio
    19.07.2016 09:27
    +1

    Огромное человеческое спасибо!
    Готовлюсь к собеседованию — и в планах было выделить сегодняшний день на ES2015. А тут ты с переводом. Как знал! как знал.


    1. EtnoLover
      19.07.2016 10:12

      Очень рад, что перевод оказался вовремя.
      P.S. Для собеседования детали лучше посмотреть в учебнике Ильи Кантора, там больше подробностей.


    1. Aingis
      19.07.2016 16:31

      Опыт говорит, что на собеседованиях про ES6 практически не спрашивают. Максимум: «работал?» или что-то вроде «как работает let». Хотя, на мой взгляд, знать полезно. Порой function foo(...args) {...} или Array.from(...) писать проще чем [].prototype.slice.call(...) и т.п.


      1. titulusdesiderio
        19.07.2016 16:48

        Согласен. У меня и нет цели освоить весь современный стандарт за сегодня. Хочу лишь ознакомиться. А такая краткая выжимка — самое подходящее для этой цели.


  1. Igogo2012
    19.07.2016 10:42
    -1

    В ECMAScript 3/5 это поведение стало возможным изменить, присвоив значение this другой переменной.

    Вы видимо про Function.prototype.bind не слышали?


    1. mx2000
      19.07.2016 12:04

      Вы видимо не слышали, что поддержка bind появилась только в 2011 году.


      1. rock
        19.07.2016 12:49

        1. mx2000
          19.07.2016 19:56

          поддержка bind появилась в Firefox 4.0 в 2011 году. С остальными браузерами примерно та же фигня. Дата выпуска спецификации совершенно минорна в данном контексте.


          1. rock
            19.07.2016 20:18

            До сих пор полной поддержки ES6 нет ни в одном стабильном браузере, а если брать в расчёт до сих пор живые IE… вот только ES6 используется чуть менее, чем повсеместно. Function#bind достаточно хорошо, хоть и не полностью, полифилится, чем все с самого начала и пользовались. Так что-что, а поддержка браузерами, в отличии от спецификации, в данном контексте, как вы выразились, "совершенно минорна".


            1. mx2000
              19.07.2016 21:54
              -1

              С самого начала повсеместно было именно self = this, потому что это очевидное и простое решение. А уже потом, с появлением prototypejs публика прозрела, что оказывается JS — это Scheme в Cи-подобном синтаксисе. Вернитесь в 2005 год и расскажите мне, что там у вас полифилится и насколько хорошо.


              1. rock
                19.07.2016 22:13
                -1

                Вы с кем и о чем спорите? Проспитесь.


            1. Igogo2012
              22.07.2016 11:37
              -1

              При чем тут вообще ES6? Разговор о ES 3/5 и .bind


              1. rock
                22.07.2016 11:56
                +1

                Действительно, какой смысл сравнивать реализацию стандартной библиотеки ES5 и ES6? Зачем приводить такой пример — эта статья ведь совершенно не о ES6, верно?


    1. EtnoLover
      19.07.2016 13:13
      +3

      Про Function.prototype.bind я слышал, спасибо.

      Здесь есть два момента:
      1. Это перевод, в котором не было написано про bind
      2. Насколько я понимаю, суть текста верна — для решения проблемы можно использовать приём с var self = this.

      В связи с перечисленными двумя пунктами, в чём конкретно у вас претензия к тексту? В отстутствии упоминания про Function.prototype.bind?


  1. iShatokhin
    19.07.2016 10:57
    +5

    Отличный материал, но стоило бы упомянуть, что у стрелочных функций нет не только своего this, но и своих arguments (а также super и new.target).


    1. EtnoLover
      19.07.2016 13:18

      По мере чтения оригинального текста у меня появлялось желание что-то дописать, переписать, задать другую структуру. Но особо себя в этом не поощрял, за исключением нескольких случаев.
      Думаю, что можно создать PR автору, он достаточно оперативно их рассматривает (мой PR рассмотрел в тот же день).

      В любом случае спасибо за полезный комментарий.


  1. MaxKitsch
    19.07.2016 10:57
    +3

    «rest» (от переводчика: не нашёл подходящего перевода на русский язык, из примера ниже всё станет ясно)
    Я бы предположил «остаток» или, по созвучию, «хвост» — использование rest-оператора в качестве единственного агрумента функции — это вырожденный случай, в общем виде это что-то вроде

    function(a, b, c, ...moreArgs) {...}

    Т.е. оператор возвращает в виде массива оставшиеся неописанными аргументы.


    1. EtnoLover
      19.07.2016 13:21
      +1

      Спасибо за пример. В моём понимании, «остаток» или «хвост» ничего не говорят сами по себе. Вы написали, что означает «хвост», и мне стало понятно. Без объяснения это было бы неочевидно. В итоге просто поменялся бы «rest» на «хвост», но смысл бы очевидней не стал. ИМХО, конечно.


      1. DangelZM
        26.07.2016 13:47

        Потому пример приведенный MaxKitsch более правильный для понимания rest


  1. vmb
    19.07.2016 11:16
    +1

    Спасибо за перевод. Пожалуйста, проверьте:

    1. Не работают ссылки на разделы (а код страницы показывает отсутствие якорей в заголовках).
    2. В начале раздела про шаблоны странное:

    \${… }`` — зачем-то экранирование и ${… } вынесено за кавычки
    ``` — зачем-то тройные кавычки


    1. EtnoLover
      19.07.2016 13:22
      +1

      Спасибо за комментарий. Ссылки поправлю вечером.


      1. vmb
        19.07.2016 21:11

        К сожалению, ссылки всё ещё не работают. Я сам пробовал атрибуты id и name в заголовках, а также <a name='...'></a> перед заголовками, но Хабр это всё вычищает, хоть в исходном коде для редактирования сохраняет. Не могу понять, что нужно сделать, чтобы получалось как здесь, например.


        1. EtnoLover
          20.07.2016 09:29
          +1

          Аналогично. У них в коде как раз id и name, но в моей публикации это почему-то не работает.
          Вчера минут 40 потратил на то, чтобы попробовать различные варианты, но все не работали.
          Простите, что не исправил. Буду разбираться дальше, а то непорядок.


          P.S. Какое совпадение, я тоже ориентировался на дайджест фронтенд-разработки)


        1. EtnoLover
          20.07.2016 22:53
          +1

          Поправил, теперь ссылки рабочие!
          За объяснение спасибо alexzfort.


          1. vmb
            20.07.2016 22:57

            А в чём секрет?


            1. EtnoLover
              21.07.2016 09:46
              +2

              В использовании anchor для указания якоря:


              <a href="#1">Заголовок 1</a>
              <anchor>1</anchor>

              Это даже можно увидеть в правом верхнем углу поля набора сообщения — "html-теги". Там про anchor подробно написано)


              1. vmb
                21.07.2016 10:43

                Спасибо.


  1. FlashManiac
    19.07.2016 12:03

    А как быть с приватными членами в классах?


    1. RubaXa
      19.07.2016 12:35

      Как и раньше — никак.
      Используйте TS, либо извращайтесь через Symbol'ы.


    1. Zenitchik
      19.07.2016 13:09

      Согласно философии JavaScript: если не хотите обращаться к членам объекта из других объектов — не обращайтесь к ним.


      1. impwx
        19.07.2016 18:13

        Помимо сказанного RubaXa и Zenitchik, есть еще пара вариантов:

        • Использовать конвенцию, префиксуя приватные члены класса символом нижнего подчеркивания
        • Обернуть всё в функцию, используя ее локальные переменные для хранения приватных данных (с помощью замыканий)

        Стоит заметить, что TS ограничивает доступ к приватным переменным только на уровне проверки типов. Они все равно будут доступны из кода на чистом JS, или при приведении объекта к типу any.


      1. FlashManiac
        19.07.2016 21:26

        Зачем тогда вообще нужен этот новый стандарт? В принципе классический JS он самодостаточен и очень гибок. И все эти прибамбасы особо сильно не упрощают жизнь. Самым логичным было бы улучшить статический анализатор до уровня когда практически все ошибки были бы сразу видны на этапе компиляции. Так как сейчас в JS это самая большая головная боль на мой взгляд. И именно из-за того, что сложно делать объемные приложения на JS выдумывают всякие препроцессоры и новые стандарты.


        1. Zenitchik
          20.07.2016 19:21

          Классовое наследование — это хороший синтаксический сахар. Одна строчка вместо трёх.
          Лично мне не нравится, что системную переменную super ввели только для классов. Общая идея JavaScript предполагает, что раз есть системная переменная, то она всегда должна быть чему-то равна. Как это сделано для this.


          Вы, кстати, писали когда-нибудь объёмное приложение на JavaScript? Я с не вижу в этом особой головной боли. А препроцессоры считаю ересью.


          1. FlashManiac
            22.07.2016 13:37

            Да, писал большие и маленькие проекты. И в каждом проекте использовал разные стили — и мне это очень понравилось. Стили выбирал исходя из задачи. Если это сложный проект — писал в стандартном стиле классы с наследованием через прототипы — каждый класс в отдельном файле. Если же что то простое — писал модулем в одном файле. Единственное, что напрягало — так это то, что не всегда видны ошибки — только при запуске, а так же IDE показывают непонятно что ))).

            С IDE вообще беда — ни одна среда пока что не может нормально обработать класс и отобразить что то адекватное в автокомплите. После долгих переборов IDE остановился пока что на VisualStudio. Там какой то хитрый автокомплит, что походу дела среда запускает некоторые куски кода, и получаем более адекватные подсказки при написании кода. Остальные же редакторы заточены под определенный стиль.


    1. ChALkeRx
      19.07.2016 18:19
      +1

      Справедливости для: приватные члены — просто соглашение. В тех же плюсах его нарушить достаточно просто. В js вы можете для этого использовать либо Symbol, либо по-старинке через нижнее подчёркивание, причём и то и другие — тоже соглашение и тоже запросто нарушается. Ну либо можно всё через замыкания и/или WeakMap делать, но получится страшно.


      1. Demogor
        21.07.2016 08:13

        На замыканиях — это вообще классика.
        Кстати, иногда неплохо затрудняет отладку какого-нибудь обфусцированного кода, когда, к примеру, нужно вызвать напрямую какой-нибудь «приватный» метод.


        1. raveclassic
          21.07.2016 09:07

          В принципе это можно сделать прямо в дебаггере из консоли, если поставить брейкпоинт на любом месте класса. Консоль подхватывает текущий контекст this и само замыкание.


          1. Demogor
            21.07.2016 10:15

            Конечно. Но для срабатывания брейкпоинта метод должен быть вызван тем или иным способом. К примеру:

            function test(){
              var innerFunction=function(){
                 console.log("You can't call me outside");
              };
            
              this.outer=function(){
                 console.log("You can call me, but for what?");
              };
              
              setTimeout(innerFunction, 1000);
            }
            


            Сферический пример в вакууме.
            innerFunction вызовется 1 раз и нанай. А если для отладки нужно ее запустить раз 5. к примеру? Ну или не отладки. а каких-нибудь личных хитровымудренных нужд(к примеру. для автоматизации каких-либо процессов на сайте при помощи userscri[t'ов).


            1. raveclassic
              21.07.2016 15:30

              Ну ставите брейкпоинт на строку с setTimeout и вызываете сначала test(), а потом innerFunction() сколько нужно


    1. Large
      19.07.2016 22:52

      Это пока на stage 1


  1. Darkside73
    19.07.2016 13:18
    +3

    Еще полезное применение деструктуризации аргументов функции:


    function userId({id}) {
      return id;
    }
    
    let user = {id: 1}
    
    userId(user)


  1. maks1mp
    19.07.2016 13:23
    +2

    в стрелочных функциях пример можно еще короче записать

    let arr = ['apple', 'banana', 'orange'];

    let breakfast = arr.map(fruit => fruit + 's');

    alert(breakfast);


    1. xaolas
      20.07.2016 13:58

      Лучше так:

      const arr = ['apple', 'banana', 'orange'];
      const breakfast = arr.map(fruit => `${fruit}s`);
      console.log(breakfast);


  1. LeusMaximus
    19.07.2016 13:35

    Может быть стоит связаться с автором, чтобы он разместил в своем репозитории ссылку на репозиторий с переводом?


    1. EtnoLover
      19.07.2016 13:35

      Это уже сделано. Ссылка на перевод уже находится в его репозитории)


      1. LeusMaximus
        19.07.2016 13:37

        Да, извиняюсь. Сразу не заметил.


  1. ChALkeRx
    19.07.2016 14:35
    +4

    При использовании const рекомендуется использовать ПРОПИСНЫЕ_БУКВЫ.

    Кем рекомендуется? Укажите ссылку, пожалуйста.


    let nicknames = ['di', 'boo', 'punkeye'];
    nicknames.size = 3;
    for (let nickname of nicknames) {
    console.log(nickname);
    }


    По-хорошему, стоит использовать const для всех переменных, которые не планируется переприсваивать:


    const nicknames = ['di', 'boo', 'punkeye'];
    nicknames.size = 3;
    for (const nickname of nicknames) {
        console.log(nickname);
    }

    Функции, записанные с помощью ключевого слова static, используются для объявления статических свойств класса.

    Пока что только только методов. Более общие свойства — в stage-1.


    1. ChALkeRx
      19.07.2016 14:42
      +1

      А, извините, это перевод. Надо будет задать эти вопросы в оригинальный репозиторий, если не забуду.


      Upd: по крайней мере последний вопрос — к переводу, в оригинале:


      Functions defined using the static keyword implement static/class functions on the class.


      1. EtnoLover
        19.07.2016 15:38

        А как бы перевели вы эту фразу? ("Functions defined using the static keyword implement static/class functions on the class")


    1. EtnoLover
      19.07.2016 14:46

      Спасибо за комментарии.
      По поводу прописных букв написано на сайте MDN в части с примерами, если переводить дословно — "использование всех прописных букв является общим соглашением".
      "Common convention is to use all-uppercase letters"


      1. ChALkeRx
        19.07.2016 15:14
        +1

        Документация с сайта MDN работает по принципу вики, и хотя в целом она адекватна, там есть весьма спорные вещи. Не следует ей всегда слепо верить.


        За ссылку спасибо. =)


        1. ChALkeRx
          19.07.2016 15:20
          +1

          Конкретно эту правку внёс https://github.com/agutoli, и это был его единственный вклад в MDN.


          https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const$compare?from=845755&to=871843


          Оттуда она и перекочевала в ваш документ.


          На самом деле общепринятая практика — это eslint c prefer-const, с которым в коде почти все переменные будут const. И прописными буквами их все никто не называет.


        1. EtnoLover
          19.07.2016 15:37

          Спасибо большое! Не знал насчёт документации MDN.


    1. gearbox
      19.07.2016 15:02

      Ну как бы прописные для констант имхо со времен С идет. Вы потом в коде как определять будете что это константа и в нее писать не надо? Держать в голове? Зачем?


      1. ChALkeRx
        19.07.2016 15:10

        Дело в том, что в коде на яваскрипте большая часть переменных — фактически константы (не переприсваиваются). Вы предлагаете всё именовать большими буквами?


        1. EtnoLover
          19.07.2016 15:34
          +3

          Мне нравится подход, описанный на сайте learn.javascript.ru:


          Константы, которые жёстко заданы всегда, во время всей программы, обычно пишутся в верхнем регистре. Например: const ORANGE = "#ffa500".


          Большинство переменных – константы в другом смысле: они не меняются после присвоения. Но при разных запусках функции это значение может быть разным. Для таких переменных можно использовать const и обычные строчные буквы в имени.


          1. ChALkeRx
            19.07.2016 15:36
            +1

            То, что написано там — имеет смысл, да. Причём не только для переменных, но и для свойств объектов.


            Но к ключевому слову const это имеет очень слабое отношение.


        1. gearbox
          19.07.2016 16:22
          +1

          Нет конечно (хотя про большую часть я бы поспорил). Но переменная, которая не переприсваивается — это не константа. Она не переприсваивается в силу алгоритма работы и при смене алгоритма может быть изменена. Константа — не переприсваивается в силу алгоритма и при доработках — надо иметь в виду что изначально в нее не планировалось ничего писать — не потому что так вышло при реализации а потому что так изначально планировалось. И этот момент неплохо бы видеть при внесении правок. Поэтому данные из конфига — обычно в верхнем регистре. А временные переменные, рабочие лошадки — в нижнем. Но это соглашение, не более того. Оно придумано для облегчения жизни, и если его соблюдение лично Вам жизнь не облегчает — нет смысла его придерживаться.


          1. ChALkeRx
            19.07.2016 17:17
            +1

            Нет конечно (хотя про большую часть я бы поспорил).

            Зашёл в первый попавшийся проект, посчитал — 90.6% const, 9.4% let. Общую статистику по всем подводить как-то не хочется, но если хотите — посчитайте =). Но в таком проекте, где с var слезли уже.


            Всё, что вы дальше говорите — к const не имеет отношения. Дело как раз в том, что const — это не то же самое множество, что константы, которые именуются большими буквами. Те могут быть как var в ES5, так и вообще свойствами какого-то объекта вроде MyLib.CONSTANT_NAME. А const как раз используется для тех переменных, которые не собираются переприсваивать, и говорить «для переменных const названия пишите в верхнем регистре» — глупость несусветная.


            Пример:


            for (const name of names) {
              …
            }

            Тут я точно знаю с первого взляда на код, что name внутри блока — всегда один и тот же и не изменяется волшебным алгоритмом. Я его что, дожен NAME назвать?


            1. gearbox
              20.07.2016 16:37

              и говорить «для переменных const названия пишите в верхнем регистре» — глупость несусветная.

              А кто такое сказал?


              Я его что, дожен NAME назвать?

              Нет, Вы мне ничего не должны. Я Вам тоже.


              1. ChALkeRx
                20.07.2016 22:11
                +1

                А кто такое сказал?

                Да в статье ж русским по белому написано:


                При использовании const рекомендуется использовать ПРОПИСНЫЕ_БУКВЫ.


                1. gearbox
                  21.07.2016 12:46
                  -1

                  При использовании const рекомендуется

                  и говорить «для переменных const названия пишите в верхнем регистре»

                  разницу видите? Вы сами придумали глупость и сами ее глупостью назвали.


                  1. ChALkeRx
                    21.07.2016 17:20
                    +1

                    [зануда-моуд]
                    Хорошо. Рекомендовать использовать прописные буквы для именования всех без разбора const-переменных — глупость.
                    [/зануда-моуд]


                    Так лучше?


                    1. gearbox
                      21.07.2016 21:16

                      Вы рано зануду выключили, я только прогреваться стал )


                      Рекомендация — то же, что совет, пожелание или предписание, высказанное в необязательной форме


                      С вики конечно, но сути не меняет.
                      Необязательная форма подразумевает нестрогое исполнение, то есть по сути — по обстоятельствам. А никак не всех без разбора. Поэтому есть рекомендации, есть стандарты, в стандартах не просто так расписывают в начале should, must, may.


                      1. ChALkeRx
                        21.07.2016 22:49
                        +1

                        Да, но только отношение const к константам, которые действительно стоит именовать заглавными буквами — примерно как между тапками и красным. Тапки бывают красные, бывают не красные. Красные бывают тапки, бывают не тапки.


                        Так стоит ли в статье-инструкции по рукоделию для новичков включать пункт «тапки красьте в красный цвет»?


                        1. raveclassic
                          22.07.2016 00:37

                          Тапки бывают красные, бывают не красные. Красные бывают тапки, бывают не тапки.

                          Какая классная фраза!
                          А по теме, тоже придерживаюсь именования заглавными констант уровня модуля, а «на месте» — строчными.


                        1. gearbox
                          22.07.2016 14:50

                          Новички — это будущие профессионалы и не надо к ним относиться как к бабочкам. Есть краска, есть тапки, есть кисточка. Они могут красить тапки в красный цвет, могут не красить. А могут и не тапки. Могут не кисточкой. Могут забить на программирование и пойти красить заборы — тоже польза.


                          1. EtnoLover
                            22.07.2016 16:22

                            Добавил опрос про const.


                            1. ChALkeRx
                              22.07.2016 20:49
                              +1

                              А правильный ответ — накатать им тикет в апстрим. Но мне пока чуть не до этого.


    1. Large
      19.07.2016 22:58

      Еще геттеры/сеттеры можно


  1. dom1n1k
    19.07.2016 15:47
    +3

    Symbol не понимаю


    1. NeoCode
      19.07.2016 16:29
      +1

      Да, тоже интересно было бы что бы кто нибудь понимающий раскрыл суть этой концепции.
      Я не разработчик на JavaScript, но языки программирования вообще — мой интерес, и поэтому я сталкивался с этой концепцией где-то еще… но так и не понял в чем фишка.
      А вообще очень приятные фичи, рад за JavaScript.


    1. impwx
      19.07.2016 18:30
      +1

      Допустим, у вас есть объект { x: 1 }. Поскольку строки сравниваются по значению, любой, кто в состоянии написать букву x, будет иметь доступ к этому свойству объекта.

      С другой стороны, каждый Symbol является уникальным:

      var a = Symbol("a");
      var b = Symbol("a");
      
      console.log(a === a); // true
      console.log(a === b); // false
      

      У Symbol есть еще две особенности. Во-первых, его можно (и нужно!) использовать в качестве ключа объекта. Кроме него ключами могут быть только числа и строки, никакие другие объекты использовать нельзя. Во-вторых, при перечислении содержимого объекта (for(var x in obj) ...) свойства с символьными ключами не указываются.

      Таким образом, можно создать уникальный идентификатор свойства и сохранить его в локальную переменную. Свойство будет содержаться в объекте, но обратиться к нему можно будет только если есть доступ к этому идентификатору. Получаются «почти что» приватные поля:

      function createPerson(age) {
          var agePty = Symbol("age");
          var person = { };
          person[agePty] = age; // обращение по уникальному Symbol
          person.getAge = function() { return person[agePty]; };
          person.birthday = function() { person[agePty]++; };
      
          return person; // agePty больше не видна
      }
      
      var me = createPerson(21);
      
      person.getAge(); // 21
      
      person.birthday();
      person.getAge(); // 22
      
      me["age"]; // undefined - недоступно
      me[Symbol("age")]; // также недоступно
      

      Я говорю «почти» приватные, потому что список символов в объекте всё равно можно получить через специальный API.


      1. dom1n1k
        19.07.2016 19:24
        +1

        Ну если это аналог private-свойств у экземпляров классов, то… как-то это костылями попахивает, честно говоря.


        1. impwx
          19.07.2016 21:35
          -2

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


        1. raveclassic
          20.07.2016 00:40
          +1

          Не совсем. Вы можете через символ скрыть какую-то сервисную информацию на объекте, которую потом, имея доступ к этому символу (например через импорт в другом модуле), эту информацию по символу из объекта достать.
          Ну и, опять же, каждый символ уникален, даже с одним и тем же описанием. Это дает гарантии на уникальность полей.
          Мне, например, приглянулся способ «наладить общение» между декораторами в библиотеке, когда у одного из них есть доступ к символу другого, чтобы скорректировать по наличию этого символа на прототипе свою работу.


          1. dom1n1k
            20.07.2016 02:32

            А как это будет фолбечиться в ES5?


            1. raveclassic
              20.07.2016 09:33
              +1

              Есть вменяемый полифилл для символов в составе core-js. Ну и на MDN достаточно радостные новости


    1. Large
      19.07.2016 23:05
      +1

      Полезен чтоб разделить методы и данные, убрать коллизию имен, привести API к одному виду. Хороший замах на перегрузку операторов (возможно появится как раз благодаря символу (уже сейчас можно переопределять например for of с помощью Symbol.iterator)).


  1. Large
    19.07.2016 23:10

    Если рассматривать это как шпаргалку — хорошо бы добавить разделы про Reflection и Proxy.


  1. yarkov
    20.07.2016 00:03

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


  1. Demogor
    20.07.2016 00:28
    +1

    Спасибо большое за перевод, весьма интересно.
    Небольшой хинт — spread еще весьма удобно использовать для фокусов в стиле пуша массива в массив:

    var a=[1,2,3];
    var b=[4,5];
    a.push(...b); //a==[1,2,3,4,5];


    1. preston_f
      21.07.2016 13:28
      +1

      Есть же старый добрый «concat»

      a = a.concat(b);

      Кроме того у push`a есть ограничение на количество аргументов (для FF 47 — 5e5, для Ya 16 — 2.5e5).
      Да и работает concat куда быстрее — jsfiddle


      1. iShatokhin
        22.07.2016 00:48
        +1

        concat создает новый массив. Также spread работает не только с массивами, но и массиводоподобными элементами, например, arguments.

        p.s. «a.push.apply(a, b)» работает быстрее, чем concat.


        1. preston_f
          25.07.2016 00:53
          +1

          У concat тоже есть плюс — он может объединять сразу больше 2-х массивов. А приведенный вами же пример «a.push.apply(a, b)» сводит на нет, ваш первый комментарий. Т.к. «apply» также прекрасно работает с массиводоподобными объектами.

          P.S. spread работает не с массиводоподобными, а с итерируемыми объектами! Т. е. если просто создадите объект с числовой последовательность в качестве ствойств и свойство «length», то spread работать не будет ( Exception: TypeError: obj is not iterable ).


  1. yarkov
    22.07.2016 10:13

    Эх… Еще бы Python-овские срезы массива добавили, а не эти slice()


    1. ChALkeRx
      22.07.2016 10:31

      Напишите proposal =).