Как стать автором
Обновить

Введение в ECMAScript 6 (ES-2015)

Время на прочтение24 мин
Количество просмотров29K

Введение в ES6



Оглавление
1. Template literals
2. let and const
3. Arrow function expressions
4. For...of
5. Computed property names
6. Object.assign()
7. Rest parameters
8. Default parameters
9. Destructuring assignment
10. Map
11. Set
12. Classes
13. Promise
14. Iterators
15. Generators
16. Sumbol

Template literals (Template strings)


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

Шаблонные литералы заключены в обратные кавычки (` `) вместо двойных или одинарных. Они могут содержать подстановки, обозначаемые знаком доллара и фигурными скобками (${выражение}). Выражения в подстановках и текст между ними передаются в функцию. По умолчанию функция просто объединяет все части в строку. Если перед строкой есть выражение (здесь это tag), то шаблонная строка называется «теговым шаблоном». В этом случае, теговое выражение (обычно функция) вызывается с обработанным шаблонным литералом, который вы можете изменить перед выводом. Для экранирования обратной кавычки в шаблонных литералах указывается обратный слэш \.

Многострочные литералы
Символы новой строки являются частью шаблонных литералов. Используя обычные строки, вставка переноса потребовала бы следующего синтаксиса:
console.log('string text line 1\n' +
'string text line 2');
// "string text line 1
//  string text line 2"

То же с использованием шаблонных литералов:
console.log(`string text line 1
string text line 2`);
// "string text line 1
//  string text line 2"


Интерполяция выражений
Для вставки выражений в обычные строки вам пришлось бы использовать следующий синтаксис:
var a = 5;
var b = 10;
console.log('Fifteen is ' + (a + b) + ' and not ' + (2 * a + b) + '.');
// "Fifteen is 15 and not 20."


Теперь, при помощи шаблонных литералов, вам доступен `синтаксический сахар`, делающий подстановки вроде той более читабельными:
var a = 5;
var b = 10;
console.log(`Fifteen is ${a + b} and not ${2 * a + b}.`);
// "Fifteen is 15 and not 20."


Вложенные шаблоны
Временами, вложить шаблон — это кратчайший и, возможно, более читабельный способ составить строку. Просто поместите внутрь шаблона с обратными кавычками ещё одни, обернув их в подстановку ${ }. Например, если выражение истинно, можно вернуть шаблонный литерал.
В ES5:
var classes = 'header'
classes += (isLargeScreen() ?
   '' : item.isCollapsed ?
     ' icon-expander' : ' icon-collapser');

В ES2015 с шаблонными литералами без вложения:
const classes = `header ${ isLargeScreen() ? '' :
    (item.isCollapsed ? 'icon-expander' : 'icon-collapser') }`;

В ES2015 с вложенными шаблонными литералами:
const classes = `header ${ isLargeScreen() ? '' :
`icon-${item.isCollapsed ? 'expander' : 'collapser'}` }`;


Теговые шаблоны
Расширенной формой шаблонных литералов являются теговые шаблоны. Они позволяют разбирать шаблонные литералы с помощью функции. Первый аргумент такой функции содержит массив строковых значений, а остальные содержат выражения из подстановок. В итоге, функция должна вернуть собранную строку (или что-либо совсем иное, как будет показано далее). Имя функции может быть любым.
var person = 'Mike';
var age = 28;

function myTag(strings, personExp, ageExp) {
  var str0 = strings[0]; // "That "
  var str1 = strings[1]; // " is a "

  // Технически, в конце итогового выражения
  // (в нашем примере) есть ещё одна строка,
  // но она пустая (""), так что пропустим её.
  // var str2 = strings[2];

  var ageStr;
  if (ageExp > 99){
    ageStr = 'centenarian';
  } else {
    ageStr = 'youngster';
  }

  // Мы даже можем вернуть строку, построенную другим шаблонным литералом
  return `${str0}${personExp}${str1}${ageStr}`;
}

var output = myTag`That ${ person } is a ${ age }`;

console.log(output);// That Mike is a youngster

Функция тега не обязана возвращать строку.

Сырые строки
Специальное свойство raw, доступное для первого аргумента тегового шаблона, позволяет получить строку в том виде, в каком она была введена, без интерпритации.
function tag(strings) {
  return strings.raw[0];
}

tag`string text line 1 \\n string text line 2`;
// выводит "string text line 1 \\n string text line 2",
// включая 'n' и два символа '\'

Вдобавок, существует метод String.raw(), возвращающий точно такую же исходную строку, какую вернула бы функция шаблона по умолчанию и строковая конкатенация вместе.

В ECMAScript 2016 теговые шаблоны следуют правилам экранирования следующих символов:
  • символы Unicode, начинающиеся с "\u", например, \u00A9
  • точки кода Unicode, начинающиеся с "\u{}", например, \u{2F804}
  • шестнадцатеричные представления символов, начинающиеся с "\x", например, \xA9
  • восьмеричные представления символов, начинающиеся с "\", например, \251​​​​​​


let and const


Областью видимости переменных, объявленных ключевым словом let и const, является блок, в котором они объявлены, и все его под блоки. В этом работа директивы let схожа с работой директивы var. Основная разница заключается в том, что областью видимости переменной, объявленной директивой var, является вся функция, в которой она объявлена. К тому же переменные, значение констант не может быть изменено новым присваиванием, а также не может быть переопределено. При декларации переменной с ключевым словом const необходимо использовать оператор присваивания для задания значения константе.

Глобальные константы не становятся свойствами объекта window, в отличие от var-переменных.
Инициализация константы обязательна.
Необходимо указать значение одновременно с объявлением (смысл в том, что потом это значение изменить уже нельзя).
Обьевление переменных с ключевым словом const создаёт константу (новую именованную ссылку на область памяти), доступную только для чтения.
Это не означает, что указываемое значение неизменно, но это означает, что идентификатор не может быть переназначен. Например, если константа указывает на объект, то сам объект может быть изменён.
Имена констант не могут совпадать с именами функций или переменных той же области видимости.

Пример отличия глобальной и блочной области видимости:
function myFunction() {
    if (true)
        let a = 5;
    console.log(a); //SyntaxError так как a доступна только в блоке if
    if (true)
        var b = 5;
    console.log(b); //5 так как b доступна во всей функции
}


Arrow function expressions


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

Базовый синтаксис
(param1, param2, …, paramN) => { statements }
(param1, param2, …, paramN) => expression
// эквивалентно: (param1, param2, …, paramN) => { return expression; }

// Круглые скобки не обязательны для единственного параметра:
(singleParam) => { statements }
singleParam => { statements }

// Функция без параметров нуждается в круглых скобках:
() => { statements }
() => expression 
// Эквивалентно: () => { return expression; }


Расширенный синтаксис
// Когда возвращаете литеральное выражение объекта, заключите тело в скобки
params => ({foo: bar})

// Rest параметры и параметры по умолчанию поддерживаются
(param1, param2, ...rest) => { statements }
(param1 = defaultValue1, param2, …, paramN = defaultValueN) => { statements }

// Деструктуризация тоже поддерживается
var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
f();  // 6

Больше информации о стрелочных функциях, вы можете наити тут.

For...of


Оператор for...of выполняет цикл обхода итерируемых объектов (включая Array, Map, Set, объект аргументов и подобных), вызывая на каждом шаге итерации операторы для каждого значения из различных свойств объекта.

Почему НЕ СЛЕДУЕТ использовать for...in цикл для итерации? Потому что в зависимости от движка, JavaScript может итерировать в произвольном порядке, что может привести к непредсказуемому результату. Если свойство изменяется за одну итерацию, а затем изменяется снова, его значением в цикле является его последнее значение. Свойство, удаленное до того, как до него дошёл цикл, не будет участвовать в нём. Свойства, добавленные в объекты в цикле, могут быть пропущены. В общем, лучше не добавлять, изменять или удалять свойство из объекта во время итерации, если по нему ещё не прошли. Нет гарантии, что добавленное свойство будет посещено циклом, изменное после проведения изменений, а удалённое после удаления. Кроме того, итерационная переменная представляет собой строку, а не число, а значит, если вы собираетесь производить какие-либо подсчеты с переменной, вам потребуется провести конкатенацию строк вместо добавления. Так что во избежание логических ошибок не следует его использовать!

В отличие от for...of цикла, цикл for...in возвращает все перечисляемые свойства, включая имеющие нецелочисленные имена и наследуемые.

Синтаксис цикла for...of
for (var i of arr) //for (переменная of объект)
    arr[i] = "something value"

Примеры:
Object.prototype.objCustom = function() {}; 
Array.prototype.arrCustom = function() {};

let iterable = [3, 5, 7];
iterable.foo = 'hello';

for (let i in iterable)
    console.log(i); // 0, 1, 2, "foo", "arrCustom", "objCustom"

for (let i in iterable) {
    if (iterable.hasOwnProperty(i))
        console.log(i); // выведет 0, 1, 2, "foo"
}

for (let i of iterable)
    console.log(i); // 3, 5, 7

Каждый объект унаследует метод objCustom и каждый массив Array унаследует метод arrCustom благодаря созданию их в Object.prototype и Array.prototype. Объект iterable унаследует методы objCustom и arrCustom из-за наследования через прототип.

Цикл выводит только перечисляемые свойства объекта iterable, в порядке их создания. Он не выводит значения 3, 5, 7 и hello поскольку они не являются перечисляемыми. Выводятся же имена свойств и методов, например, arrCustom и objCustom.

Цикл аналогичен предыдущему, но использует hasOwnProperty() для проверки того, собственное ли это свойство объекта или унаследовано. Выводятся только собственные свойства. Имена 0, 1, 2 и foo принадлежат только экземпляру объекта (не унаследованы). Методы arrCustom и objCustom не выводятся поскольку они унаследованы.

Этот цикл обходит iterable и выводит те значения итерируемого объекта которые определены в способе его перебора, т.е. не свойства объекта, а значения массива 3, 5, 7.

Computed property names


Синтаксис объявления объектов и их элементов поддерживает вычисляемые имена свойств. Это позволяет добавлять в скобки [] выражение, которое будет вычислено, как имя свойства. Оно напоминает шаблонные литералы.

Пример вычисляемых имён:
var a = () => "world";

var a = {
	["a" + (10 - 6)]: {
		["some" + "string"]: true,
		[10 + 20]: 10,
		[`hello ${a()}`]: a()
	}
}



Object.assign()


Метод Object.assign() используется для копирования значений всех собственных перечисляемых свойств из одного или более исходных объектов в целевой объект. После копирования он возвращает целевой объект.

Метод Object.assign() копирует из исходных объектов в целевой объект только перечисляемые и собственные свойства. Он использует внутренний метод [[Get]] на исходных объектах и внутренний метод [[Set]] на целевом объекте, так что он также вызывает геттеры и сеттеры. Именно поэтому он присваивает свойства вместо простого копирования или определения новых свойств. Это поведение может сделать метод непригодным для вливания новых свойств в прототип, если вливаемые исходные объекты содержат геттеры. Вместо него для копирования в прототипы определений свойств, включая признак их перечисляемости, следует использовать методы Object.getOwnPropertyDescriptor() и Object.defineProperty().

Копируются свойства типов как String, так и Symbol.

В случае возникновения ошибки, например, когда свойство является незаписываемым, возбуждается исключение TypeError, а целевой объект target остаётся неизменным.

Обратите внимание, что метод Object.assign() не выкидывает исключения, если в качестве исходных значений выступают null или undefined.

Пример: клонирование объекта
var obj = { a: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }

Пример: слияние объектов
var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };

var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1);  // { a: 1, b: 2, c: 3 }, изменился и сам целевой объект.


Rest parameters


Синтаксис оставшихся параметров функции позволяет представлять неограниченное множество аргументов в виде массива.

Если последний именованный аргумент функции имеет префикс ..., то он автоматически становится массивом с элементами от 0 до theArgs.length в соответствии с актуальным количеством аргументов, переданных в функцию.

Синтаксис
function(a, b, ...theArgs) {
  // ...
}


Пример использования синтаксиса оставшихся параметров:
function name (a, b, ...c) {}
name (0, 1, 2, 3,)

В этом примере a = 0 b = 1 c[0] = 2 c[1] = 3
Если оставшийся параметр не передан, то он будет пустым массивом (в отличии от обычных параметров он никогда не будет undefined).

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

var a = [ 0, 1, 2 ];
var b = [ 3, 4, 5 ];

var c = [ ...a, ...b ]; // [ 0, 1, 2, 3, 4, 5 ]


Default parameters


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

В JavaScript параметры функции, которым при ее вызове не передаются значения, принимают по умолчанию значение undefined. Однако в некоторых случаях может быть полезно задать иное значение по умолчанию. Именно для таких случаев предназначены параметры по умолчанию.

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

Пример использования параметров по умолчанию:
function myFun(a=5) {
	return a*a;
}

console.log(myFun()); // 25


Destructuring assignment


Синтаксис деструктурирующего присваивания в выражениях JavaScript позволяет извлекать данные из массивов или объектов при помощи синтаксиса, подобного объявлению массива или литералов в объекте.

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

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

Пример деструктурирующего присваивания:
var a, b, rest;
[a, b] = [1, 2];
console.log(a); // 1
console.log(b); // 2

[a, b, ...rest] = [1, 2, 3, 4, 5];
console.log(a); // 1
console.log(b); // 2
console.log(rest); // [3, 4, 5]

({a, b} = {a:1, b:2});
console.log(a); // 1
console.log(b); // 2

Больше примеров можете увидеть тут.

Map


Map — Объект, содержащий пары ключ-значение и сохраняющий порядок вставки. Любое значение (как объекты, так и примитивы) могут быть использованы в качестве ключей.

Пример:
var myMap = new Map();

var keyObj = {},
    keyFunc = function () {},
    keyString = "a string";

// задание значений
myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, "value associated with keyObj");
myMap.set(keyFunc, "value associated with keyFunc");

myMap.size; // 3

// получение значений
myMap.get(keyString);    // "value associated with 'a string'"
myMap.get(keyObj);       // "value associated with keyObj"
myMap.get(keyFunc);      // "value associated with keyFunc"

myMap.get("a string");   // "value associated with 'a string'"
                         // потому что keyString === 'a string'
myMap.get({});           // undefined, потому что keyObj !== {}
myMap.get(function() {}) // undefined, потому что keyFunc !== function () {}

Отличие Map от Object:

  • Ключами Объекта выступают Строки и Символы, в то время как любое значение может быть ключом Map, включая функции, объекты и примитивы.
  • В отличие от Объектов, ключи в Map упорядочены. Таким образом, во время итерации Map, ключи возвращаются в порядке вставки.
  • Вы легко можете получить количество элементов в Map с помощью свойства size, в то время как количество элементов Объекта может быть определено только вручную.
  • Map — итерируемый объект и может быть итерирован напрямую, в то время как Объект требует ручного получения списка ключей и их итерации.
  • Объект имеет прототип и поэтому имеет стандартный набор ключей, который, при неосторожности, может пересекаться с вашими ключами. С момента выхода ES5 это может быть изменено с помощью map = Object.create(null).
  • Map может иметь более высокую производительность в случаях частого добавления или удаления ключей.

Свойства и методы:

  • Map.prototype.size — Возвращает число пар ключ\значение на Map
  • Map.prototype.set(key, value) — Добавляет переданную пару ключ\значение в Map. Если указанный ключ уже существует то он перезапишется новым значением.
  • Map.prototype.get(key) — Возвращает значение переданного ключа. Если ключа нет, то вернётся undefined
  • Map.prototype.has(key) — Возвращает true если переданный ключ существует и false если его нет
  • Map.prototype.delete(key) — Удаляет указанную пару ключ\значение и возвращает true. Возвращает false если ключ не существует
  • Map.prototype.clear() — Удаляет все пары ключ\значение из Map
  • Map.prototype.keys() — Возвращает итератор ключей на Map для каждого элемента
  • Map.prototype.values() — Возвращает итератор значений на Map для каждого элемента
  • Map.prototype.entries() — Возвращает итератор массива [key, value] на Map для каждого элемента


Set


Позволяет сохранять уникальные значения любого типа, как примитивы, так и другие типы объектов.

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

Своиства и методы экземпляров Set
  • size — Возвращает количество элементов в объекте Set.
  • add(value) — Добавляет новый элемент с заданным значением в объект Set. Возвращает объект Set.
  • clear() — Удаляет все элементы из объекта Set.
  • delete(value) — Удаляет элемент, связанный со значением, и возвращает значение, которое has (value) ранее вернул бы. has (value) вернет false позже.
  • entries() — Возвращает новый объект Iterator, который содержит массив [value, value] для каждого элемента в объекте Set в порядке вставки. Это сохраняется аналогично объекту Map, так что каждая запись имеет одинаковое значение для своего ключа и значения здесь.
  • forEach(callbackFn[, thisArg]) — Вызывает callbackFn один раз для каждого значения, присутствующего в объекте Set, в порядке вставки. Если для thisEach указан параметр thisArg, он будет использоваться в качестве значения this для каждого обратного вызова.
  • has(value) — Возвращает логическое значение, подтверждающее, присутствует ли элемент с заданным значением в объекте Set или нет.
  • values() — Возвращает новый объект Iterator, который содержит значения для каждого элемента в объекте Set в порядке вставки.

Использование объекта Set
var mySet = new Set();

mySet.add(1); // Set { 1 }
mySet.add(5); // Set { 1, 5 }
mySet.add(5); // Set { 1, 5 }
mySet.add("some text"); // Set { 1, 5, 'some text' }
var o = {a: 1, b: 2};
mySet.add(o);

mySet.add({a: 1, b: 2}); // переменная o связана с другим объектом, поэтому данная строка также сработает

mySet.has(1); // true
mySet.has(3); // false, 3 не было добавлено в set
mySet.has(5);              // true
mySet.has(Math.sqrt(25));  // true
mySet.has("Some Text".toLowerCase()); // true
mySet.has(o); // true

mySet.size; // 5

mySet.delete(5); // удаляет 5 из set
mySet.has(5);    // false, 5 было удалено

mySet.size; // 4, было удалено одно значение
console.log(mySet); // Set {1, 'some text', Object {a: 1, b: 2}, Object {a: 1, b: 2}}

Обход Set
// выведет элементы по порядку: 1, "some text", {"a": 1, "b": 2}
for (let item of mySet) console.log(item);

// выведет элементы по порядку: 1, "some text", {"a": 1, "b": 2}
for (let item of mySet.keys()) console.log(item);
 
// выведет элементы по порядку: 1, "some text", {"a": 1, "b": 2}
for (let item of mySet.values()) console.log(item);

// выведет элементы по порядку: 1, "some text", {"a": 1, "b": 2} 
//(key и value в данном случае одинаковы)
for (let [key, value] of mySet.entries()) console.log(key);

// преобразует Set в Array
var myArr = Array.from(mySet); // [1, "some text", {"a": 1, "b": 2}]

// следующее будет работать при запуске с HTML документом
mySet.add(document.body);
mySet.has(document.querySelector("body")); // true

// преобразования из Array в Set и обратно
mySet2 = new Set([1,2,3,4]);
mySet2.size; // 4
[...mySet2]; // [1,2,3,4]

// пересечение можно представить следующим образом  
var intersection = new Set([...set1].filter(x => set2.has(x)));

// разность можно представить следующим образом
var difference = new Set([...set1].filter(x => !set2.has(x))); 

// Обход элементов set при помощи forEach
mySet.forEach(function(value) {
  console.log(value);
});

// 1
// 2
// 3
// 4



Classes


Классы в JavaScript были введены в ECMAScript 2015 и представляют собой синтаксический сахар над существующим в JavaScript механизмом прототипного наследования. Синтаксис классов не вводит новую объектно-ориентированную модель, а предоставляет более простой и понятный способ создания объектов и организации наследования.

На самом деле классы — это «специальные функции», поэтому точно также, как вы определяете функции (function expressions и function declarations), вы можете определять и классы с помощью: class declarations и class expressions.

Разница между объявлением функции (function declaration) и объявлением класса (class declaration) в том, что объявление функции совершает подъём (hoisted), в то время как объявление класса — нет. Поэтому вначале необходимо объявить ваш класс и только затем работать с ним, а иначе будет сгенерировано исключение типа ReferenceError.

Объявление класса


Первый способ определения класса — class declaration (объявление класса). Для этого необходимо воспользоваться ключевым словом class и указать имя класса (в примере — «myClass»).
class myClass{
    constructor(height, width) {
        this.height = height;
        this.width = width;
    }
}

Второй способ определения класса — class expression (выражение класса). Можно создавать именованные и безымянные выражения. В первом случае имя выражения класса находится в локальной области видимости класса и может быть получено через свойства самого класса, а не его экземпляра.
// безымянный
var myClass = class {
    constructor(height, width) {
        this.height = height;
        this.width = width;
    }
};

// именованный
var myClass = class myClass {
    constructor(height, width) {
        this.height = height;
        this.width = width;
    }
};

Выражения класса подвержены тем же проблемам с подъёмом (hoisting), что и объявления класса!

Тела объявлений классов и выражений классов выполняются в строгом режиме (strict mode).
Строгий режим изменяет синтаксис и поведение среды исполнения.
Если быть точнее, то строгий режим характеризуется следующим:
  • преобразование ошибок в исключения;
  • изменения, упрощающие вычисление переменной в определённых случаях использования её имени;
  • изменения, упрощающие eval и arguments;
  • изменения, упрощающие написание «безопасного» JavaScript;

Преобразование ошибок в исключения
  1. Ошибки времени исполнения будут переобразованны в необработанные исключения.
  2. Невозможно случайное создание глобальных переменных.
  3. Присваивания, которые всё равно завершились бы неудачей, выбрасывают исключения.
  4. Попытки удалить неудаляемые свойства будут вызывать исключения (в то время как прежде такая попытка просто не имела бы эффекта).
  5. Все свойства, перечисленные в сериализованном объекте, должны встречаться только один раз. В обычном коде имена свойств могут дублироваться, а значение свойства определяется последним объявлением.
  6. Имена аргументов в объявлении функций встречались только один раз. В обычном коде последний повторённый аргумент скрывает предыдущие аргументы с таким же именем.
  7. Запрещён синтаксис восьмеричной системы счисления.
  8. Запрещёна установка свойств с primitive значениями.

Упрощение работы с переменными
Строгий режим упрощает сопоставление имени переменной с местом ее определения в коде.
  1. Запрещено использование with. Проблема с with в том, что во время выполнения любое имя внутри блока может ссылаться как на свойство обрабатываемого объекта, так и на переменную в окружающем (или даже в глобальном) контексте — невозможно знать об этом заранее. Простая альтернатива with уже существует — присваивание объекта переменной с коротким именем и затем доступ к нужному свойству как свойству этой переменной.
  2. eval() в строгом режиме не добавляет новых переменных в окружающий контекст.
  3. Запрещено удаление простых имён.

Упрощение eval и arguments
Строгий режим снижает количество странностей в поведении arguments и eval, оба из которых примешивают определённое количество магии в обычный код. Так eval добавляет или удаляет переменные и меняет их значения, а переменная arguments может удивить своими проиндексированными свойствами, которые являются ссылками (синонимами) для проименованных аргументов функции.
  1. Ключевые слова eval и arguments не могут быть переопределены или изменены.
  2. Поля объекта arguments не связаны с проименованными аргументами функции, а являются их продублированными копиями значений.
  3. Свойство arguments.callee не поддерживается. В обычном коде свойство arguments.callee ссылается на саму функцию для вызова которой и был создан объект arguments.

«Обезопасенный» JavaScript
Некоторые веб-сайты предоставляют пользователям возможность писать JavaScript, который будет выполняться на сайте от имени других пользователей. В браузерах, JavaScript может иметь доступ к приватной информации, что является дырой в безопасности JavaScript.
  1. Значение, передаваемое в функцию как this, в строгом режиме не приводится к объекту.
  2. Не представляется возможным осуществлять «прогонку» стека JavaScript посредством базовых расширений ECMAScript.
  3. В функциях свойство arguments больше не предоставляет доступ к переменным созданным внутри функции.

Вы можете посмотреть спецификацию ECMAScript 5.1, что бы узнать больше о «Strict mode».
А тагже документацию от Mozilla.

Конструкторы


constructor — специальный метод, служащий для создания и инициализации объектов, созданных с использованием class.
class Student {
	constructor(name) {
    	    this.name = name;
  	}
}
var robert = new Student('Robert');
console.log(robert.name);  // Outputs 'Robert'

При созданий новых объектов от класса будет запускаться constructor(), который необходим для инициализации объектов.

В классе может быть только один метод с именем «constructor». Если класс содержит более одного конструктора, будет сгенерировано исключение SyntaxError.

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

Если вы не определили метод constructor, то будет использован конструктор по умолчанию.
Для базовых классов, constructor по умолчанию:
constructor() {}

Для производных классов, constructor по умолчанию:
constructor(...args) {
  super(...args);
}


Методы


Синтаксис обьявления методов:
var obj = {
    property([parameters]) {},
    get property() {},
    set property(value) {},
    * generator() {}
};

Сокращение методов-генераторов
var obj = { 
    * g() {
        var index = 0;
        while(true)
            yield index++;
    }
};
var it = obj.g();
console.log(it.next().value); // 0
console.log(it.next().value); // 1


Все определения методов кроме методов-генераторов не могут быть конструкторами и будут выбрасывать TypeError если вы попытаетесь создать их экземпляр.

Вычисляемые имена свойств
var obj = {
    ["foo" + 2](){ return 2; }

console.log(obj.foo2()); // 2
};


Ключевое слово static, определяет статические методы для класса. Статические методы вызываются без инстанцирования их класса, и не могут быть вызваны у экземпляров (instance) класса.

Cинтаксис getter'ов и setter'ов
class Student {
	constructor(name) {
    	        this.name = name;
  	}
  	get Name() {
  		return this.name;
  	}
  	set Name(newName) {
  		if(typeof(newName) != "string")
  			throw new Error("Name is not a string!");
  		else
  			this.name = newName; // Robert
  	}
}
var robert = new Student('robert');
robert.Name = "Robert";
console.log(robert.Name);

  • setter — Необходим для валидаций записываемых параметров (как в примере выше)
  • getter — Необходим для получения свойств (хотя их можно получить и напрямую). Не может иметь аргументов

В ES6 нет встроенной инкапсуляций, но ее можно организовать самому. Например, вот так:

var Student = (function () {
	let privateProps = new WeakMap();

	class Person {
    	        constructor(name, Age) {
      		        this.name = name; // public
      		        privateProps.set(this, {age: Age}); // private
    	        }
    	        get Age() {
      		        return privateProps.get(this).age;
    	        }
    	        set Age (newAge) {
    		        privateProps.set(this, {age: newAge});
    	        }
  	}
  	return Person;
})();
var robert = new Student('Robert', 19);
robert.Age = 20;
console.log(robert.Age); // 20


Наследование


Ключевое слово extends используется в объявлениях классов и выражениях классов для создания класса, дочернего относительно другого класса.

class Person {
	constructor (age) {
		this.age = age;
	}
	sayAge () {
		return this.age;
	}
}
class Student extends Person {
	constructor (name, age) {
		super(age);
		this.name = name;
	}
	sayFull () {
		return `Hello my name is ${this.name} and I'm ${super.sayAge()} years old`;
	}
}
var robert = new Student("Robert", 19);
console.log(robert.sayFull()); // Hello my name is Robert and I'm 19 years old


В конструкторе ключевое слово super() используется как функция, вызывающая родительский конструктор. Её необходимо вызвать до первого обращения к ключевому слову this в теле конструктора. Ключевое слово super также может быть использовано для вызова функций родительского объекта.

При переопределение в дочернем классе методов родительского класса, по умолчанию будет вызыватся методы дочернего класса, но можно явно вызвать методы родительского класса используя функцию super().
class obj {
  constructor(name){
    this.name = name;
  }
  
  displayName(){
    return this.name.length;
  }
}
undefined
class obj_2 extends obj {
  constructor(name) {
    super(name);
  }
  
  displayName() { // Переопределение метода родительского класса
    return [this.name, super.displayName()];
  }
}

var Obj = new obj_2("obj_2");
console.log(Obj.displayName()); // Array [ "obj_2", 5 ]


Расширение встроенных объектов с помощью extends
Этот пример расширяет встроенный объект Date.
class myDate extends Date {
  constructor() {
    super();
  }

  getFormattedDate() {
    var months = [
      'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
      'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
    ];
    return this.getDate() + '-' +
      months[this.getMonth()] + '-' +
      this.getFullYear();
  }
}


Promise


Объект Promise (промис) используется для отложенных и асинхронных вычислений.

Promise может находиться в трёх состояниях:
  • ожидание (pending): начальное состояние, не выполнено и не отклонено.
  • выполнено (fulfilled): операция завершена успешно.
  • отклонено (rejected): операция завершена с ошибкой.

При создании промис находится в ожидании (pending), а затем может стать исполненным (fulfilled), вернув полученный результат (значение), или отклоненным (rejected), вернув причину отказа. В любом из этих случаев вызывается обработчик, прикрепленный к промису методом then. (Если в момент назначения обработчика промис уже исполнен или отклонен, обработчик все равно будет вызван, т.е. асинхронное исполнение промиса и назначение обработчика не будет происходить в «состоянии гонки», как, например, в случае с событиями в DOM.)

Так как методы Promise.prototype.then() и Promise.prototype.catch() сами возвращают промис, их можно вызывать цепочкой, создавая соединения.
image

Создание обещаний (promise)
Объект Promise создается при помощи ключевого слова new и своего конструктора. Конструктор Promise принимает в качестве аргумента функцию, называемую «исполнитель» (executor function). Эта функция должна принимать две функции-коллбэка в качестве параметров. Первый из них (resolve) вызывается, когда асинхронная операция завершилась успешно и вернула результат своего исполнения в виде значения. Второй коллбэк (reject) вызывается, когда операция не удалась, и возвращает значение, указывающее на причину неудачи, чаще всего объект ошибки.
const myPromise = new Promise((resolve, reject) => {
    // выполняется асинхронная операция, которая в итоге вызовет:
   resolve(someValue); // успешное завершение
  // или
  reject("failure reason"); // неудача
});

Объект функции с двумя аргументами resolve и reject вызывает успешное выполнение обещания, второй отклоняет его.

Чтобы снабдить функцию функционалом обещаний, нужно просто вернуть в ней объект Promise.
function myAsyncFunction(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.onload = () => resolve(xhr.responseText);
    xhr.onerror = () => reject(xhr.statusText);
    xhr.send();
  });
}

С помощью. then прикрепляются обработчики выполнения и отклонения обещания.

Методы
  • Promise.all(iterable) — Ожидает исполнения всех промисов или отклонения любого из них. Возвращает промис, который исполнится после исполнения всех промисов в iterable. В случае, если любой из промисов будет отклонен, Promise.all будет также отклонен.
  • Promise.allSettled(iterable) — Ожидает завершения всех полученных промисов (как исполнения, так и отклонения). Возвращает промис, который исполняется когда все полученные промисы завершены (исполнены или отклонены), содержащий массив результатов исполнения полученных промисов.
  • Promise.race(iterable) — Ожидает исполнения или отклонения любого из полученных промисов. Возвращает промис, который будет исполнен или отклонен с результатом исполнения первого исполненного или отклонённого промиса из .iterable.
  • Promise.reject(reason) — Возвращает промис, отклонённый из-за reason.
  • Promise.resolve(value) — Возвращает промис, исполненный с результатом value.

Прототип Promise
  • catch(onRejected) — Добавляет функцию обратного вызова, для обработки отклонения обещания, которая возвращает новое обещание выполненное с переданным значением, если она вызвана, или оригинальное значение resolve, если обещание выполнено.
  • then(onFulfilled, onRejected) — Добавляет обработчик выполнения и отклонения обещания, и возвращает новое обещание выполненное со значением вызванного обработчика, или оригинальное значение, если обещание не было обработано (т.е. если соответствующий обработчик onFulfilled или onRejected не является функцией).


Cоздание асинхронного http запроса:
const URL = "https://getfestivo.com/v1/holidays?api_key=f8f42551-eb66-49d2-bcba-b8e42727ddfb&country=US&year=2019"; // Этот API возвращает список праздников различных стран

function asyncHttpRequest (url) {
  return new Promise((resolve, reject) => { // Возвращаем promise
  if (url == undefined) // Если вдруг забыли передать url
    reject(new Error("Expected url and received nothing"));
  else {
    resolve(() => {
      fetch(url).then((response) => { // Создаём запрос
        return response.json(); // Извлекаем содержимое тела JSON из ответа
  	  }).then((myJson) => {
    	    return(console.log(myJson)); // Выводим результат запроса в консоль
  	  });
	});
      }
    }
);}

asyncHttpRequest(URL).then((result) => result(), (error) => console.log(error));



Больше примеров и информации о промисах вы можете получить в официальной документации, а тагже документации Mozilla.

Iterators


Обработка каждого элемента коллекции является весьма распространенной операцией. JavaScript предоставляет несколько способов перебора коллекции, от простого цикла for до map(), filter() и array comprehensions. Итераторы и генераторы внедряют концепцию перебора непосредственно в ядро языка и обеспечивают механизм настройки поведения for...of циклов.

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

После создания, объект-итератор может быть явно использован, с помощью вызовов метода next().

Iterable — Это объект, содержание которого можно перебрать.
Итерируемый объект отличается от не итерируемого тем что имеет специальный метод, который возвращает объект, для доступа к которому используется специальный символ: Symbol.iterator
Iterable {
    [Symbol.iterator]()
}

Обьект возврощяющий этод метод формально называется итератор.
У итератора есть всего лишь один метод next()
Iterator {
    next();
}

Который возвращает объект (назовем его itreratorResult) c двумя свойствами done и value
IteratorResult {
    done,
    value
}

done указывает есть ли еще значение в перебираемой последовательности, а value содержит следующий элемент последовательности.


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

Объект является итерируемым, если в нем определен способ перебора значений, то есть, например, как значения перебираются в конструкции for..of. Некоторые встроенные типы, такие как Array или Map, по умолчанию являются итерируемыми, в то время как другие типы, как, например, Object, таковыми не являются.

Чтобы быть итерируемым, объект обязан реализовать метод iterator, что означает, что он (или один из объектов выше по цепочке прототипов) обязан иметь свойство с именем Symbol.iterator.

Так выглядит стандартный итератор:
function makeIterator(array){
    var nextIndex = 0;
    
    return {
       next: function(){
           return nextIndex < array.length ? { value: array[nextIndex++], done: false } : { done: true };
       }
    }
}


Generators


Генераторы — это специальный тип функции, который работает как фабрика итераторов. Функция становится генератором, если содержит один или более yield операторов и использует function* синтаксис.

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

Давайте рассмотрим обычную функцию, выполняющую некоторые математические вычисления и возвращающую результат:
function myFunction (a) {
	a = a*2;
	a = a - 1;

	return a;
}

console.log(myFunction(5)); // 9

Теперь взгляните на аналогичную функцию генератор:
function* generator(a) {
	a = a*2;
	yield a;
	a = a - 1;
	yield a;
}

var it = generator(5);

console.log(it.next().value); // 10
console.log(it.next().value); // 9</i>

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

Функции генераторы предоставляют мощный инструмент для написания сложных последовательных функций.

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

Метод next() также принимает значение, которое может использоваться для изменения внутреннего состояния генератора. Значение, переданное в next(), будет рассматриваться как результат последнего yield выражения, которое приостановило генератор.

Можно заставить генератор выбросить исключение, вызвав его метод throw() и передав в качестве параметра значение исключения, которое должно быть выброшено. Это исключение будет выброшено из текущего приостановленного контекста генератора так, будто текущий приостановленный yield оператор являлся throw оператором.

Если yield оператор не встречается во время обработки выброшенного исключения, то исключение передается выше через вызов throw(), и результатом последующих вызовов next() будет свойство done равное true.

У генераторов есть метод return(value), который возвращает заданное значение и останавливает работу генератора.

Symbol


Symbol​ (Символ) — примитивный тип данных, экземпляры которого уникальны и неизменяемы.

В среде выполнения JavaScript значение «символа» создается путем вызова функции Symbol (), которая динамически создает анонимное и уникальное значение. Единственное разумное использование — сохранить символ, а затем использовать сохраненное значение для создания свойства объекта.

Когда символ используется как идентификатор в присваивании свойства, свойство (например, символ) является анонимным; а также не исчислимым. Поскольку свойство не исчислимо, оно не будет отображаться в цикле «for (… in ...)», и поскольку свойство является анонимным, оно не будет отображаться в массиве результатов «Object.getOwnPropertyNames ()». Доступ к этому свойству можно получить с помощью исходного значения символа, создавшего его, или путем итерирования в массиве результатов «Object.getOwnPropertySymbols ()».


Так вы можете создать символьное свойство:

var user = {
	name: "Alex",
	[Symbol("password")]: "12hsK3I"
}

Для получения массива символьных объектов используйте свойство Object.getOwnPropertySymbols(obj);
Для получения доступа в любом месте вашего кода используйте методы Symbol.for() и Symbol.keyFor()

Для получения дополнительной информации о типе данных Sumbol, загляните в официальную документацию, а тагже в документацию Mozilla.
Теги:
Хабы:
+15
Комментарии35

Публикации

Изменить настройки темы

Истории

Работа

Ближайшие события