Для навигации используйте пробел или стрелки клавиатуры.
Esc для обзора слайдов.
За авторством Adevgo Ltd. Исходный код доступен на GitHub.
В июне 2015-го года был принят новый стандарт EcmaScript: ES2015, также известный как ES6.
Ключевое слово let позволяет объявлять переменные с ограниченной областью видимости - только для блока {...}, в котором происходит объявление. Это называется блочной областью видимости. Вместо ключевого слова var, которое обеспечивает область видимости внутри функции, стандарт ES6 рекомендует использовать let.
var a = 2;
{
let a = 3;
console.log(a); // 3
let a = 5;
// TypeError: Identifier 'a' has already been declared
}
console.log(a); // 2
JS Bin
Другой формой объявления переменной с блочной областью видимости является ключевое слово const. Оно предназначено для объявления переменных (констант), значения которых доступны только для чтения. Это означает не то, что значение константы неизменно, а то, что идентификатор переменной не может быть переприсвоен.
{
const B = 5;
B = 10; // TypeError: Assignment to constant variable
const ARR = [5, 6];
ARR.push(7);
console.log(ARR); // [5,6,7]
ARR = 10; // TypeError: Assignment to constant variable
ARR[0] = 3; // value is mutable
console.log(ARR); // [3,6,7]
}
JS Bin
for (let i=0;i<10;i++) {/* ... */};
console.log(i); // → RefferenceError: i is not defined
JS Bin
Стрелочные функции представляют собой сокращённую запись функций в ES6. Стрелочная функция состоит из списка параметров ( ... ), за которым следует знак => и тело функции.
// Classical Function Expression
let addition = function(a, b) {
return a + b;
};
// Implementation with arrow function
let addition = (a, b) => a + b;
JS Bin
Заметим, что в примере выше, тело функции представляет собой краткую запись, в которой не требуется явного указания на то, что мы хотим вернуть результат. Возможно использование блока из фигурных скобок.
let arr = ['apple', 'banana', 'orange'];
let breakfast = arr.map(fruit => {
return fruit + 's';
});
console.log(breakfast); // ['apples', 'bananas', 'oranges']
JS Bin
Стрелочные функции не просто делают код короче. Они тесно связаны с ключевым словом this и привязкой контекста. Поведение стрелочных функций с ключевым словом this отличается от поведения обычных функций с this. Каждая функция в JavaScript определяет свой собственный контекст this, но внутри стрелочных функций значение this то же самое, что и снаружи (стрелочные функции не имеют своего this).
function Person() {
// The Person() constructor defines `this`
// as an instance of itself.
this.age = 0;
setInterval(function growUp() {
// In non-strict mode, the growUp() function
// defines `this` as the global object, which
// is different from the `this`
// defined by the Person() constructor.
this.age++;
}, 1000);
}
var p = new Person();
setInterval(() => {
setTimeout(() => {
console.log(p.age);
}, 1000);
}, 1000);
// 0
JS Bin
В ECMAScript 3/5 это поведение стало возможным изменить, присвоив значение this другой переменной.
function Person() {
var self = this;
self.age = 0;
setInterval(function growUp() {
// The callback refers to the `self` variable of which
// the value is the expected object.
self.age++;
}, 1000);
}
setInterval(() => {
setTimeout(() => {
console.log(p.age);
}, 1000);
}, 1000);
// 2 3 4...
JS Bin
Как сказано выше, внутри стрелочных функций значение this то же самое, что и снаружи, поэтому следующий код работает так, как от него и ожидается:
function Person() {
this.age = 0;
setInterval(() => {
setTimeout(() => {
this.age++;
// `this` properly refers to the person object
}, 1000);
}, 1000);
}
var p = new Person();
setInterval(() => {
setTimeout(() => {
console.log(p.age);
}, 1000);
}, 1000);
// 1 2 3 4...
JS Bin
const result = [1, 2, 3].map(num => num * 2);
console.log(result);
// [2, 4, 6]
JS Bin
const result = [1, 2, 3, 4].map((num, i) => num * 2 + i);
console.log(result);
// [2, 5, 8, 11]
JS Bin
this
не может быть изменен с помощью .call
или .apply
и т.п.arguments
используются аргументы внешней "обычной" функции.ES6 позволяет установить параметры по умолчанию при объявлении функции
let getFinPrice= (price, tax = 0.7) => price + price * tax;
getFinPrice(500); // 850
JS Bin
...
оператор называют как spread или rest, в зависимости от того, как и где он используется. При использовании в любом итерируемом объекте (iterable), данный оператор "разбивает" ("spread") его на индивидуальные элементы:
function foo(x, y, z) {
console.log(x, y, z);
}
let arr = [1, 2, 3];
foo(...arr); // 1 2 3
JS Bin
Другим распространённым использованием оператора ...
является объединение набора значений в один массив. В данном случае оператор работает как "rest" ("соединяет с остальными элементами")
function foo(...args) {
console.log(args);
}
foo(1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]
JS Bin
function sum () {
const nums = Array.prototype.slice.call(arguments);
const multiplier = nums.shift();
const base = nums.shift();
const sum = nums.reduce((accum, num) => {
return accum + num;
}, base);
return multiplier * sum;
}
const total = sum(2, 6, 10, 8, 9);
console.log(total);
// 66
JS Bin
function sum (multiplier, base, ...nums) {
var sum = nums.reduce((accum, num) => accum + num, base);
return multiplier * sum;
}
const total = sum(2, 6, 10, 8, 9);
console.log(total);
// 66
JS Bin
ES6 позволяет объявить литералы объекта с помощью короткого синтаксиса для инициализации свойств из переменных и определения функциональных методов. Также, стандарт обеспечивает возможность вычисления свойств непосредственно в литерале объекта.
function getCar(make, model, value) {
return {
// with property value shorthand syntax, you can omit
// the property value if key matches variable name
make, // same as make: make
model, // same as model: model
value, // same as value: value
// computed values now work with object literals
['make' + make]: true,
// Method definition shorthand syntax omits
// `function` keyword & colon
depreciate() { this.value -= 2500; }
};
}
JS Bin
let car = getCar('Kia', 'Sorento', 40000);
console.log(car);
// {
// make: 'Kia',
// model:'Sorento',
// value: 40000,
// makeKia: true,
// depreciate: function()
// }
JS Bin
В ES6 появилась новая поддержка для восьмеричных и двоичных литералов. Добавление к началу числа 0o или 0O преобразует его в восьмеричную систему счисления (аналогично, 0b или 0B преобразует в двоичную систему счисления).
let oValue = 0o10;
console.log(oValue); // 8
let bValue = 0b10;
console.log(bValue); // 2
JS Bin
Деструктуризация помогает избежать использования вспомогательных переменных при взаимодействии с объектами и массивами.
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
JS Bin
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
JS Bin
const bar = {
baz: "dat"
};
const { baz: foo } = bar;
console.log(foo);
// "dat"
JS Bin
const baz = {};
const { foo='bar' } = baz;
console.log(foo);
// "bar"
JS Bin
const {foo, bar: baz} = {foo: 0, bar: 1};
console.log(foo, baz);
// 0
// 1
JS Bin
const {foo: {bar}} = { foo: { bar: 'baz' } };
console.log(bar);
// "baz"
JS Bin
const {foo: {bar: deep}} = { foo: { bar: 'baz' } }
console.log(deep);
// "baz"
JS Bin
var {foo} = {}
console.log(foo);
// undefined
JS Bin
const {foo: {bar}} = {}
console.log(foo);
// TypeError: Cannot read property 'bar' of undefined
JS Bin
const [a, , b] = [0, 1, 2]
console.log(a, b);
// 0
// 2
JS Bin
let a = 13;
let b = 42;
[a, b] = [b, a]
console.log(a, b);
// 42
// 13
JS Bin
function foo ({ a=1, b=2 } = {}) {
console.log(a, b);
}
const props = { a: 23 };
foo(props);
// 23
// 2
foo();
// 1
// 2
JS Bin
function getUrlParts (url) {
var re =
/^(https?):\/\/(example\.com)(\/articles\/([a-z0-9-]+))$/
return re.exec(url)
}
const url = 'http://example.com/articles/some-article'
const parts = getUrlParts(url);
const [protocol,host,pathname,slug] = parts;
console.log(protocol, host, pathname, slug);
// 'http'
// 'example.com'
// '/articles/some-article'
// 'some-article'
JS Bin
const savedFile = {
extension: 'jpg',
name: 'repost',
size: 14040
};
function fileSummary({name, extension, size}) {
return `The ${name}.${extension} is of size ${size} B`;
}
const result = fileSummary(savedFile);
console.log(result);
// "The repost.jpg is of size 14040 B"
JS Bin
const companies = [
'Google',
'Facebook',
'Yandex'
];
const [firstCompany, ...otherCompanies] = companies;
console.log(firstCompany, otherCompanies);
// "Google"
// ["Facebook", "Yandex"]
JS Bin
const companies = [
{ name: 'Google', location: 'Mountain View' },
{ name: 'Facebook', location: 'Menlo Park' },
{ name: 'Uber', location: 'San Francisco' }
];
const [{location: google}] = companies;
console.log(google);
// "Mountain View"
JS Bin
const points = [
[4, 5],
[10, 1],
[0, 40]
];
const result = points.map(([x, y]) => {
return { x, y }
});
console.log(result);
// [{x: 4, y: 5}, {x: 10, y: 1}, {x: 0, y:40}]
JS Bin
import React, { Component, PropTypes } from 'react';
import { includes } from 'lodash';
vs
import includes from 'lodash/includes';
ES6 позволяет использовать метод super в (безклассовых) объектах с прототипами. Вот простой пример:
var parent = {
foo() {
console.log("Hello from the Parent");
}
}
var child = {
foo() {
super.foo();
console.log("Hello from the Child");
}
}
Object.setPrototypeOf(child, parent);
child.foo(); // Hello from the Parent
// Hello from the Child
JS Bin
ES6 предоставяляет более простой способ вставки значения переменной или результата выражения (т.н. "интерполяцию"), которые рассчитываются автоматически.
`${ ... }`
используется для вычисления значения переменной/выражения.`
Обратные кавычки используются как разделитель.
let user = 'Kevin';
console.log(`Hi ${user}!`); // Hi Kevin!
JS Bin
Можно использовать свою функцию шаблонизации для строк.
Эта функция будет автоматически вызвана и получит в качестве аргументов строку, разбитую по вхождениям параметров ${…} и сами эти параметры.
const t = (template, ...vals) => [template, ...vals];
const foo = 42;
const bar = 13;
const [str, ...vals] = t`some text with ${foo} and ${bar}`;
console.log(str, vals);
// ["some text with ", " and ", ""]
// [42, 13]
JS Bin
for...of
используется для перебора в цикле итерируемых объектов, например, массивов.
let nicknames = ['di', 'boo', 'punkeye'];
nicknames.size = 3;
for (let nickname of nicknames) {
console.log(nickname);
}
// di
// boo
// punkeye
JS Bin
for...in
используется для перебора в цикле всех доступных для перебора (enumerable) свойств объекта.
let nicknames = ['di', 'boo', 'punkeye'];
nicknames.size = 3;
for (let nickname in nicknames) {
console.log(nickname);
}
// 0
// 1
// 2
// size
JS Bin
for (let char of "Hello") {
console.log(char); // H e l l o
}
vs
console.log(...'World'); // W o r l d
vs
const str = "Foo Bar";
let iterator = str[Symbol.iterator]();
while(true) {
let result = iterator.next();
if (result.done) break;
console.log(result.value); // F o o " " B a r
}
JS Bin
ES6 представляет новые структуры данных - Map и WeakMap. На самом деле, мы используем "Map" в JavaScript всё время. Каждый объект можно представить как частный случай Map.
Классический объект состоит из ключей (всегда в строковом виде) и значений, тогда как в Map для ключа и значения можно использовать любое значение (и объекты, и примитивы).
var myMap = new Map();
var keyString = "a string",
keyObj = {},
keyFunc = () => {};
// setting the values
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
// getting the values
myMap.get(keyString); // "value associated with 'a string'"
myMap.get(keyObj); // "value associated with keyObj"
myMap.get(keyFunc); // "value associated with keyFunc"
JS Bin
Метод set можно чейнить:
map
.set('1', 'str1')
.set(1, 'num1')
.set(true, 'bool1');
При создании Map можно сразу инициализировать списком значений.
let map = new Map([
['1', 'str1'],
[1, 'num1'],
[true, 'bool1']
]);
Для проверки значений на эквивалентность используется алгоритм SameValueZero. Он аналогичен строгому равенству ===, отличие – в том, что NaN считается равным NaN. Поэтому значение NaN также может быть использовано в качестве ключа.
Этот алгоритм нельзя изменять или задавать свою функцию сравнения.
Методы для удаления записей:
Для проверки существования ключа:
Итерация:
WeakMap это Map, в котором ключи обладают неустойчивыми связями, что позволяет не мешать сборщику мусора удалять элементы WeakMap. Это означает, что можно не беспокоиться об утечках памяти.
Стоить отметить, что в WeakMap, в отличие от Map, каждый ключ должен быть объектом.
Для WeakMap есть только четыре метода: delete(ключ), has(ключ), get(ключ) и set(ключ, значение). Нет свойства size. Нельзя перебрать элементы итератором или forEach. Нет метода clear().
let w = new WeakMap();
w.set('a', 'b');
// Uncaught TypeError: Invalid value used as weak map key
var o1 = {},
o2 = () => {},
o3 = window;
w.set(o1, 37);
w.set(o2, "azerty");
w.set(o3, undefined);
w.get(o3); // undefined, because that is the set value
w.has(o1); // true
w.delete(o1);
w.has(o1); // false
JS Bin
Объекты Set это коллекции уникальных значений. Дублированные значения игнорируются, т.к. коллекция должна содержать только уникальные значения. Значения могут быть примитивами или ссылками на объекты.
let mySet = new Set([1, 1, 2, 2, 3, 3]);
mySet.size; // 3
mySet.has(1); // true
mySet.add('strings');
mySet.add({ a: 1, b:2 });
mySet.forEach((item) => {
console.log(item);
// 1
// 2
// 3
// 'strings'
// Object { a: 1, b: 2 }
});
for (let value of mySet) {
console.log(value);
// 1
// 2
// 3
// 'strings'
// Object { a: 1, b: 2 }
}
JS Bin
Основные методы:
Аналогично 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 has not been added to the set
ws.delete(window); // removes window from the set
ws.has(window); // false, window has been removed
JS Bin
В ES6 представили новый синтаксис для классов. Здесь стоит отметить, что класс ES6 не представляет собой новую объектно-ориентированную модель наследования. Это просто синтаксический сахар для существующего в JavaScript прототипного наследования.
Класс в ES6 представляет собой просто новый синтаксис для работы с прототипами и функциями-конструкторами, которые мы привыкли использовать в ES5.
Функции, записанные с помощью ключевого слова static, используются для объявления статических свойств класса.
class Task {
constructor() {
console.log("task instantiated!");
}
showId() {
console.log(23);
}
static loadAll() {
console.log("Loading all tasks..");
}
}
console.log(typeof Task); // function
let task = new Task(); // "task instantiated!"
task.showId(); // 23
Task.loadAll(); // "Loading all tasks.."
JS Bin
class Car {
constructor (speed) {
this.speed = speed
}
}
class Tesla extends Car {
constructor (speed) {
super(speed * 2)
}
}
var newCar = new Car(100);
var newTesla = new Tesla(120);
console.log(newCar.speed); // 100
console.log(newTesla.speed); // 240
JS Bin
В ES6 ключевое слово extends позволяет классу-потомку наследовать от родительского класса. Важно отметить, что конструктор класса-потомка должен вызывать super().
Также, в классе-потомке можно вызвать метод родительского класса с помощью super.имяМетодаРодителя().
Функции, записанные с помощью ключевого слова static, используются для объявления статических свойств класса.
class Car {
constructor() {
console.log("Creating a new car");
}
}
class Porsche extends Car {
constructor() {
super();
console.log("Creating Porsche");
}
}
let c = new Porsche();
// Creating a new car
// Creating Porsche
JS Bin
Symbol это уникальный и неизменяемый тип данных, представленный в ES6. Целью Symbol является создание уникального идентификатора, к которому нельзя получить доступ.
var sym = Symbol("some optional description");
console.log(typeof sym); // symbol
JS Bin
Заметим, что использовать new вместе с Symbol(…) нельзя.
Если Symbol используется как свойство/ключ объекта, он сохраняется таким специальным образом, что свойство не будет показано при нормальном перечислении свойств объекта.
var o = {
val: 10,
[Symbol("random")]: "I'm a symbol",
};
console.log(Object.getOwnPropertyNames(o));
// ["val"]
console.log(Object.getOwnPropertySymbols(o));
// [Symbol(random)]
Чтобы извлечь символьные свойства объекта, нужно использовать Object.getOwnPropertySymbols(o)
JS BinИтератор обращается к элементам коллекции по одному, в то же время сохраняя память о своей текущей позиции в этой коллекции. У итератора есть метод 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]() с описанием объекта.
TutorФункции-генераторы представляют собой новую особенность ES6, которая позволяет функции создавать много значений в течение некоторого периода времени, возвращая объект (называемый генератором), который может быть итерирован для выброса значений из функции по одному за раз.
Функция-генератор возвращает итерируемый объект при своём вызове. Функция-генератор записывается с помощью знака * после ключевого слова function, а в теле функции должно присутствовать ключевое слово yield.
function *infiniteNums() {
var n = 1;
while (true) {
yield n++;
}
}
var numbers = infiniteNums(); // returns an iterable object
numbers.next(); // { value: 1, done: false }
numbers.next(); // { value: 2, done: false }
numbers.next(); // { value: 3, done: false }
Tutor
Каждый раз при вызове yield возвращённое значение становится следующим значением в последовательности.
Также заметим, что генераторы вычисляют свои возвращённые значения по запросу, что позволяет им эффективно представлять последовательности, затратные с точки зрения вычислений, или даже бесконечные последовательности.
В ES6 появилась встроенная поддержка промисов. Промис это объект, который ждёт выполнения асинхронной операции, после которого (т.е. после выполнения) промис принимает одно из двух состояний: fulfilled (resolved, успешное выполнение) или rejected (выполнено с ошибкой).
Стандартным способом создания промиса является конструктор new Promise(), который принимает обработчик с двумя функциями как параметрами. Первый обработчик (обычно именуемый resolve) представляет собой функцию для вызова вместе с будущим значением, когда оно будет готово; второй обработчик (обычно именуемый reject) является функцией, которая вызывается для отказа от выполнения промиса, если он не может определить будущее значение.
var p = new Promise((resolve, reject) => {
if (/* condition */) {
resolve(/* value */); // fulfilled successfully
} else {
reject(/* reason */); // error, rejected
}
});
Каждый промис обладает методом then, в котором есть два коллбэка. Первый коллбэк вызывается, если промис успешно выполнен (resolved), тогда как второй коллбэк вызывается, если промис выполнен с ошибкой (rejected).
p.then((val) => console.log("Promise Resolved", val),
(err) => console.log("Promise Rejected", err));
При возвращении значения от then коллбэки передадут значение следующему коллбэку then.
var hello = new Promise((resolve, reject) => {
resolve("Hello")
});
hello.then((str) => `${str} World`)
.then((str) => `${str}!`)
.then((str) => console.log(str)) // Hello World!
JS Bin
При возвращении промиса, успешно обработанное значение промиса пройдёт к следующему коллбэку, для того, чтобы эффективно соединить их вместе. Эта простая техника помогает избежать ада с коллбэками ("callback hell").
var p = new Promise((resolve, reject) => { resolve(1) });
var add1 = (val) => new Promise((resolve, reject) => {
resolve(val + 1)
});
p.then(add1)
.then(add1)
.then((val) => console.log(val)); // 3
JS Bin
new Promise(resolve => resolve({ foo: 'bar' }))
.then(result => console.log(result))
// { foo: 'bar' }
new Promise((resolve, reject) =>
reject(new Error('failed')))
.catch(reason => {
console.log(`${reason.name}: ${reason.message}`)
});
// "Error: failed"
JS Bin
Метод fetch – это XMLHttpRequest нового поколения. Он предоставляет улучшенный интерфейс для осуществления запросов к серверу: как по части возможностей и контроля над происходящим, так и по синтаксису, так как построен на промисах.
fetch('/article/fetch/user.json')
.then(function(response) {
alert(response.headers.get('Content-Type'));
// application/json; charset=utf-8
alert(response.status); // 200
return response.json();
})
.then(function(user) {
alert(user.name); // admin
})
.catch( alert );
JS Bin
Поддержка юникода
// same as ES5.1
"𠮷".length == 2
// new RegExp behaviour, opt-in ‘u’
"𠮷".match(/./u)[0].length == 2
// new form
"\u{20BB7}"=="𠮷"=="\uD842\uDFB7"
// new String ops
"𠮷".codePointAt(0) == 0x20BB7
// for-of iterates code points
for(var c of "𠮷") {
console.log(c);
}
JS Bin
Модули
// lib/math.js
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;
// app.js
import * as math from "lib/math";
alert("2π = " + math.sum(math.pi, math.pi));
// otherApp.js
import {sum, pi} from "lib/math";
alert("2π = " + sum(pi, pi));
// lib/mathplusplus.js
export * from "lib/math";
export var e = 2.71828182846;
export default function(x) {
return Math.log(x);
}
// app.js
import ln, {pi, e} from "lib/mathplusplus";
alert("2π = " + ln(e)*pi*2);
Новые константы и методы
Number.EPSILON
Number.isInteger(Infinity) // false
Number.isNaN("NaN") // false
Math.acosh(3) // 1.762747174039086
Math.hypot(3, 4) // 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2
"abcde".includes("cd") // true
"abc".repeat(3) // "abcabcabc"
"abccc".endsWith("ccc")) // true
"aaabc".startsWith("aaa") // true
Array.from(document.querySelectorAll('*'))
// Returns a real Array
Array.of(1, 2, 3)
// Similar to new Array(...),
// but without special one-arg behavior
[0, 0, 0].fill(7, 1)
// [0,7,7]
[1, 2, 3].find(x => x == 3)
// 3
[1, 2, 3].findIndex(x => x == 2)
// 1
[1, 2, 3, 4, 5].copyWithin(3, 0)
// [1, 2, 3, 1, 2]
["a", "b", "c"].entries()
// iterator [0, "a"], [1,"b"], [2,"c"]
["a", "b", "c"].keys()
// iterator 0, 1, 2
["a", "b", "c"].values()
// iterator "a", "b", "c"
Object.assign()
Функция Object.assign получает список объектов и копирует в первый target свойства из остальных.
Object.assign(target, src1, src2...)
const user = { name: "Вася" };
const visitor = { isAdmin: false, visits: true };
const admin = { isAdmin: true };
Object.assign(user, visitor, admin);
// user <- visitor <- admin
console.log( user );
// name: Вася, visits: true, isAdmin: true
JS Bin