Ecmascript 6 (2015)

# Первоклассная поддержка async I/O

Асинхронное выполнение кода сопровождало нас в течение почти всей истории языка. setTimeout, в конце концов, был введен примерно в то время, когда вышел JavaScript 1.0.

Но, пожалуй, язык не поддерживает асинхронность на самом деле. Возвращаемое значение вызовов функций, которые запланированы “выполниться в будущем” обычно равны undefined или в случае с setTimeoutNumber.

С одной стороны, вы найдете API более предсказуемым. В качестве теста, рассмотрим новое fetch API. Как это работает за сигнатурой, которую мы только что описали? Вы угадали. Оно возвращает Promise.

Если Вы использовали Node.JS в прошлом, вы знаете, что есть неформальная договоренность о том, что обратные вызовы следуют сигнатуре:

Mapping extends To Traditional JavaScript

The bigger difference comes when we try to extend the parent class with a subclass. Refer to the following code snippet:

Mapping extends keyword to traditional approach

We can see that the extends keyword takes care of extending the parent class Animal to the subclass in ES6 way, but the super keyword is also used here to make sure that Animal class is called via Gorilla’s constructor so as to inherit the characteristics and behaviors of the Animal. Here, the super keyword is used as a function to call Animal class for initializing Gorilla. Here, super is equivalent to Animal.call(this, …).

To make the same thing happen traditionally, a few additional steps are required. A function for the subclass Gorilla needs to be created as per line 10. Since the Gorilla is going to inherit the characteristics and behaviors of Animal it is a must to call the Animal’s constructor function inside Gorilla’s constructor as shown in line 11, this line is comparable to line 4 and does the same exact thing. Only we need to pass “this” reference explicitly to the Animal class to ensure the call was made from Gorilla class.

Furthermore, we need to set the Gorilla function’s prototype as a new object created from the Animal’s prototype as shown on line 11. In doing this, we are overriding Gorilla’s prototype object. Hence, on the following line 15, we need to set the constructor of Gorilla explicitly. These steps take care of setting the Gorilla class as a subclass of Animal class.

Compiling

For most cases, you can simply replace your `node` calls with `babel-node` calls. `babel-node` caches modules in memory, so there’s a significant startup delay and potentially a lot of memory usage. Consider compiling to to ES5 for production. Read on.

Babel’s docs make compiling look like a breeze:

$ babel script.js --out-file script-compiled.js

Couldn’t be easier, right? Well, if you don’t try to import any modules, sure, this will work fine. But if you do anything non trivial, you’ll want to compile your whole code base, not just one file. For that, you want to use the `-d` option.

$ babel -d build-dir source-dir

Note that the output directory comes first.

If you want the debugger to work properly, you will want to add source maps with the `-s` option:

$ babel -d build-dir source-dir -s

Doing so will tell Babel that for each file it compiles, it should also produce a source map file that will tell debuggers where to find the original source code while you’re stepping through the live code in the engine. In other words, you’ll see the code that you wrote, instead of the compiled output that Babel generated. That’s usually what you want.

To compile for the browser, you want to use Webpack, or the Babelify Browserify transform. I typically use babelify for quick compiles at the terminal. For instance, to run some unit tests:

npm install -g browserify browser-runbrowserify -t babelify script.js | browser-run -p 2222
  1. Install `browserify` and `browser-run` so that you can use them anywhere in your terminal.
  2. Create a bundle from `script.js` and run the script in chrome. Hit http://localhost:2222 from your favorite browser, and the script will run in the browser. Console log output will get piped back to the console.

Compile a bundle:

$ browserify script.js -t babelify --outfile bundle.js

Configuring Webpack is too much for this quick tutorial, but if you want to skip all the busywork, you can use this boilerplate for production modules.

JS Tutorial

JS HOMEJS IntroductionJS Where ToJS OutputJS StatementsJS SyntaxJS CommentsJS VariablesJS OperatorsJS ArithmeticJS AssignmentJS Data TypesJS FunctionsJS ObjectsJS EventsJS StringsJS String MethodsJS NumbersJS Number MethodsJS ArraysJS Array MethodsJS Array SortJS Array IterationJS DatesJS Date FormatsJS Date Get MethodsJS Date Set MethodsJS MathJS RandomJS BooleansJS ComparisonsJS ConditionsJS SwitchJS Loop ForJS Loop WhileJS BreakJS Type ConversionJS BitwiseJS RegExpJS ErrorsJS ScopeJS HoistingJS Strict ModeJS this KeywordJS LetJS ConstJS Arrow FunctionJS ClassesJS DebuggingJS Style GuideJS Best PracticesJS MistakesJS PerformanceJS Reserved WordsJS VersionsJS Version ES5JS Version ES6JS JSON

Транспиляция

Мы можем писать свой код на ES6 сегодня. Как уже упоминалось во введении, поддержка ES6 браузерами пока не очень обширна и сильно варьируется. Очень вероятно, что не всё из ES6 кода, который вы напишете, будет понято браузерами ваших пользователей. Вот почему мы должны конвертировать его в предыдущие версии JavaScript (ES5), которые хорошо запускаются на любом современном браузере. Это преобразование часто называют «транспиляция». Мы должны выполнять его с нашими приложениями, пока браузеры, которые мы хотим поддерживать, не начнут понимать ES6.

Начнем

Транспилировать код не сложно. Вы можете траспилировать код непосредственно из командной строки или можете включить в качестве плагина планировщик задач, например, Grunt или Gulp. Существует множество решений для транспиляции, в том числе и Babel, Traceur и TypeScript. Взгляните, например, на множество способов начать использовать ES6 с Babel (ранее «6to5»). Большинство возможностей ES6 в вашем распоряжении!

Теперь, когда, надеюсь, вы впечатлены ES6, почему бы не начать использовать его? В зависимости от особенностей, которые вы хотите использовать, и браузеров или оболочек, которые вам необходимо поддерживать (например, Node.js), вы, вероятно, хотите включить транспилятор в ваш рабочий процесс. И если вы готовы к этому, то существуют также наблюдатели за файлами, чтобы сделать ваш процесс разработки бесшовным.

Если вы начинаете с нуля, то возможно просто захотите конвертировать ваш код из командной строки (смотрите, например, документацию Babel CLI). Если вы уже используете планировщик задач (например, Grunt или Gulp), то можете добавить плагин (например, gulp-babel или babel-loader для Webpack). Для Grunt существует grunt-babel и много других плагинов, связанных с ES6. Люди, использующие Browserify, могут попробовать babelify.

Многие возможности могут быть преобразованы в ES5-совместимый код без существенных дополнительных усилий. Другие требуют дополнительных временных костылей (которые могут быть предоставлены транспилятором) и/или ухудшают производительность. А некоторые просто невозможны. Чтобы поэкспериментировать с кодом ES6 и посмотреть, как выглядит транспилированный код, вы можете использовать различные интерактивные инструменты (также известные как REPL):

  • Traceur: сайт, REPL
  • Babel: сайт, REPL
  • TypeScript: сайт, REPL
  • ScratchJS (расширение Chrome)

Итак, что же я могу использовать?

В общем, некоторые из возможностей ES6 могут использоваться практически «свободно», например, модули, стрелочные функции, остаточные параметры и классы. Эти функции могут быть транспилированы в ES5 без больших накладных расходов. Дополнения к объектам и прототипам , и (например, и ) требуют так называемых «заполнителей» (polyfills). Заполнители – временные костыли для функционала, который пока не поддерживается браузером. Вы можете загрузить заполнитель первым, и ваш код будет работать, как если бы браузер поддерживал этот функционал. и Babel, и Traceur предоставляют такие заполнители.

Смотрите таблицу совместимости ES6 от Kangax для полного обзора возможностей ES6, которые поддерживаются транспиляторами и браузерами. Мотивирует то, что на момент написания статьи, последние браузеры уже поддерживают от 55% до более 70% всех возможностей ES6. Microsoft Edge, Google Chrome и Mozilla Firefox здесь действительно конкурируют друг с другом, и это хорошо для веба в целом.

Лично я думаю, что возможность легко использовать новые возможности ES6, такие как модули, стрелочные функции и остаточные параметры, – это облегчение и значительное улучшение процесса кодирования.

Массивы

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

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

  • внутри функции;
  • , возвращенный методом ;
  • данные новых структур и .

В приведенном выше примере, вы можете увидеть, что у массива элементов есть метод , который недоступен в коллекции .

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

Далее у нас есть , который ведет себя так же, как конструктор . Он исправляет особый случай, когда в качестве аргумента передается одно число. Это делает более предпочтительным по сравнению с . Тем не менее, в большинстве случаев вы захотите использовать литералы array.

Последняя, но не менее важная, пара методов была добавлена для прототипа . Я думаю, что методы поиска будут очень кстати для большинства разработчиков JavaScript.

  • возвращает первый элемент, для которого функция обратного вызова вернет true;
  • возвращает индекс первого элемента, для которого функция обратного вызова вернет true;
  • «перезаписывает» элементы массива переданным аргументом.

Краткая история

Этот стандарт ECMA основан на нескольких технологиях, самые известные из которых — JavaScript (Netscape) и JScript (Microsoft). Язык был изобретен Brendan Eich в Netscape и впервые появился в браузере этой компании Navigator 2. В дальнейшем он присутствовал во всех браузерах от Netscape и всех — от Microsoft, начиная с Internet Explorer 3.0, и так — до наших дней.

Разработка этого стандарта началась в ноябре 1996г. Первая редакция стандарта ECMA была принята общим собранием ECMA в июне 1997г.

Стандарт ECMA был отослан в ISO/IEC JTC 1 для быстрого согласования и одобрен как международный стандарт ISO/IEC 16262 в апреле 1998г. В июне 1998 года общее собрание ECMA одобрило вторую редакцию ECMA-262, чтобы поддерживать соответствие с ISO/IEC 16262. Изменения между первой и второй редакцией — по сути редакторские правки.

Настоящий документ описывает третью редакцию стандарта и включает в себя мощные регулярные выражения, улучшенную поддержку строк, новые управляющие конструкции, обработку исключений try/catch, конкретизированное определение ошибок, форматирование при численном выводе и небольшие изменения для приближающейся интернационализации и будущего роста языка.

Работа над языком не завершена. Технический комитет работает над значительными улучшениями, включая механизмы для создания и использования скриптов в интернет и улучшенную координацию с другими разработчиками стандартов, такими как группы в консорциуме W3C и Wireless Application Protocol Forum.

Этот стандарт был принят как 3-я редакция ECMA-262 общим собранием ECMA
в декабре 1999 года.

Модули

Модули – это, конечно, долгожданное дополнение к языку JavaScript. Я думаю, что это главное, ради чего стоит копаться в ES6.

Сегодня любой серьезный JavaScript проект использует какой-либо тип модульной системы, возможно что-то вроде шаблона «открытый модуль» или более обширные форматы AMD или CommonJS. Тем не менее, браузеры не располагают каким-либо типом модульной системы. Вам всегда необходим либо этап сборки, либо загрузчик ваших модулей AMD или CommonJS. Инструменты для обработки этого включают в себя RequireJS, Browserify и Webpack.

Спецификация ES6 включает в себя как новый синтаксис, так и механизм загрузчика модулей. Если вы хотите использовать модули и писать на будущее, то должны использовать данный синтаксис. Современные инструменты сборки поддерживают этот формат, возможно через плагин, поэтому всё должно пройти хорошо (не беспокойтесь, мы обсудим это далее в разделе «Транспиляция»).

А теперь о синтаксисе модулей ES6. Модули разработаны вокруг ключевых слов и . Рассмотрим пример с двумя модулями сразу:

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

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

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

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

Getting started

require('es6-shim');
var assert = require('assert');

assert.equal(true, 'abc'.startsWith('a'));
assert.equal(false, 'abc'.endsWith('a'));
assert.equal(true, 'john alice'.includes('john'));
assert.equal('123'.repeat(2), '123123');

assert.equal(false, NaN === NaN);
assert.equal(true, Object.is(NaN, NaN));
assert.equal(true, - === );
assert.equal(false, Object.is(-, ));

var result = Object.assign({ a 1 }, { b 2 });
assert.deepEqual(result, { a 1, b 2 });

assert.equal(true, isNaN('a'));
assert.equal(false, Number.isNaN('a'));
assert.equal(true, Number.isNaN(NaN));

assert.equal(true, isFinite('123'));
assert.equal(false, Number.isFinite('123'));
assert.equal(false, Number.isFinite(Infinity));

// Tests if value is a number, finite,
// >= -9007199254740992 && 
assert.equal(false, Number.isInteger(2.4));

assert.equal(1, Math.sign(400));
assert.equal(, Math.sign());
assert.equal(-1, Math.sign(-400));

var found = .find(function (item) { return item  2 === 5; });
assert.equal(10, found);

var foundIndex = .findIndex(function (item) { return item  2 === 5; });
assert.equal(1, foundIndex);

// Replacement for `{}` key-value storage.
// Keys can be anything.
var map = new Map(, ]);
map.set('John', 25);
map.set('Alice', 400);
map.set(, 555);
assert.equal(undefined, map.get()); // undefined because you need to use exactly the same object.
map.delete('Alice');
map.keys();
map.values();
assert.equal(4, map.size);

// Useful for storing unique items.
var set = new Set();
set.add(2);
set.add(5);
assert.equal(true, set.has());
assert.equal(true, set.has(1));
assert.equal(true, set.has(2));
assert.equal(false, set.has(4));
assert.equal(true, set.has(5));
set.delete(5);
assert.equal(false, set.has(5));

// Promises, see
// http://www.slideshare.net/domenicdenicola/callbacks-promises-and-coroutines-oh-my-the-evolution-of-asynchronicity-in-javascript
// https://github.com/petkaantonov/bluebird/#what-are-promises-and-why-should-i-use-them
Promise.resolve(5).then(function (value) {
  assert.equal(value, 5);
  if (value) throw new Error('whoops!');
  // do some stuff
  return anotherPromise();
}).catch(function (e) {
  assert.equal(e.message, 'whoops!');
  assert.equal(true, e instanceof Error);
  // any errors thrown asynchronously end up here
});

JavaScript не такой, как все

Для начала, три концепции, которые должен включать в себя любой объектно-ориентированный язык программирования: инкапсуляция, наследование и полиморфизм. Знакомые слова, не так ли? Теперь простыми словами о каждой концепции:

Инкапсуляция

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

Что не так с инкапсуляцией в JavaScript? Всё просто. В примере приведённом выше отсутствует возможность явно обозначить приватность, как в других языках программирования с помощью ключевых слов , и (пример с PHP). Естественно, подобная проблема достаточно просто решается с помощью тех же самых функций — создаётся ещё одна обёртка над кодом в виде самовызывающейся анонимной функции, например, при использовании паттерна “модуль”:

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

С помощью наследования вы, буквально, говорите: “У меня есть один конструктор/класс и другой конструктор/класс, который точно такой же, как и первый, кроме вот этого и вот этого”. Чаще всего наследование в JavaScript реализуется с помощью функции , позволяющий создать новый объект с заданным прототипом.

И здесь с JavaScript “всё не так”. Тот же самый пример, написанный на PHP:

Да в чём вообще кроется разница? Мы же выполнили одни и те же действия, просто с разным синтаксисом! Чтобы понять, почему JavaScript другой представьте себе семейство птичек: дедушка попугай, отец попугай и сын попугай — все попугаи! Совершенно очевидно, что если у деда попугая вырастет ещё одна лапка, то это ни коем образом не повлияет на отца и сына. То есть попугай, родившийся с двумя лапами, так и останется до конца своей жизни с двумя лапами, в независимости от того, что случилось с любым из его предков. Подобным образом можно представить себе классическое наследование.

Но и это ещё не всё! Единственный оставшийся попугай (дедушка) решил, что трёх лап мало и приобрёл себе ещё одну, а также решил стать ласточкой. В результаты из обычного семейства попугаев мы получили: деда ласточку с четырьмя лапами, отца орла с тремя лапами, сына орла с двумя лапами. Вот она вся суть прототипного наследования. Похоже на безумие? Да? Тогда перейдём к коду:

Вывод из всего выше перечисленного: в JavaScript прототипное наследование “динамическое”, можно изменять всё налету, классическое же наследование подобным похвастаться не может: всё, что вы объявили в одном классе останется там навсегда. Грубо говоря, классическое представление наследования предполагает наличие определённой статической схемы, по которой будет строиться каждый объект данного класса. В прототипном наследовании мы имеем дело не со схемой, а с живым, постоянно развивающимся организмом, который со временем изменяется и принимает ту форму, которая нам нужна (и это прекрасно).

Полиморфизм

Полиморфизм проще всего постичь на примере встроенных конструкторов (, , …). Вот если вас спросят: “Чем число отличается от массива и что у них общего?”, чтобы вы ответили? Наверняка, вы были начали рассказывать про примитивы и объекты, чем они отличаются, что можно делать с теми и другими, на вопрос про отличия. Но чем они похожи друг на друга? Абсолютно разные же типы данных! Но, очевидно, что они разделяют определённую часть методов, например, метод , унаследованный от . Это уже полиморфизм? Ещё нет, но мы уже близко. Метод можно весьма успешно переназначить, во-первых, в прототипе функции конструктора, и, во-вторых, сразу же для данного конкретного объекта.

Что происходит? При использовании метода на каждом из объектов происходит проверка того, какой метод нужно выбрать. Проверка ведётся следующим образом: изначально проверяется существует ли нужное свойство на самом объекте, если существует, то используется именно оно, если же нет, то проверка продолжается — на наличие свойства проверяется прототип, потом прототип прототипа, потом прототип прототипа прототипа… и так, пока не дойдём до самого конца — (null является прототипом для ). Таким образом, полиморфизм отвечает за то, чей метод вызвать. Подробнее о том, как это всё работает в других языках программирования можно узнать в этом вопросе на Тостере, а более подробный пример с JavaScript можно посмотреть в статье про прототипы.

Большой вывод, который я хочу, чтобы вы сделали из всего вышеперечисленного: “Javascript не такой, как остальные языки программирования с классическим пониманием ООП”. Не стоит жить в стране предрассудков и пытаться перенести своё понимание ООП из другого языка — вы только потеряете время.

Теперь, когда мы во всём разобрались и сделали нужный вывод (“JavaScript другой”) можно перейти к рассмотрению “классов” в JavaScript.

Arrow Functions

Arrow functions allows a short syntax for writing function expressions.

You don’t need the keyword, the keyword, and the
curly brackets.

// ES5
var x = function(x, y) {
  
return x * y;
}
// ES6
const x = (x, y) => x * y;

Arrow functions do not have their own .
They are not well suited for defining object methods.

Arrow functions are not hoisted. They must be defined before they are used.

Using
is safer than using , because a function expression is
always constant value.

You can only omit the keyword and the curly brackets if the function is a single statement.
Because of this, it might be a good habit to always keep them:

Блочная область видимости

В ES5 есть только область видимости уровня функции (т.е. необходимо оборочивать код в функцию для создания области видимости), что всегда вызывало массу вопросов. В ES6 появилась область видимости уровня блока (область видимости в пределах фигурных скобок), для этого необходимо использовать ключевые слова let или const вместо var.

Предотвращает двойное объявление переменной

В ES6 недопустимо двойное объявление переменной, если вы объявляете ее с помощью let или const в той же области видимости. Это помогает избежать двойного определения функций из разных библиотек (как функция ‘add’ в примере ниже).

Устраняет необходимость IIFE

В ES5, как в примере ниже, мы должны были использовать немедленно вызываемые функции, чтобы избежать попадания переменных в глобальную область видимости. В ES6 мы можем просто использовать для этого фигурные скобки ({}) и ключевые слова let и const.

Babel — инструмент для конвертации ES6 в ES5

В конечном итоге у нас должна быть возможность запустить ES6 код в обычном браузере. Babel — это наиболее популярный инструмент для конвертации ES6 в ES5. Он доступен как консольная утилита, модуль Node.js или online-конвертер. Я использую модуль node.js для своих приложений и online-версию для быстрого просмотра различий.

На рисунке ниже показано, как Babel переименовывает переменные, чтобы эмулировать работу let и const!

Делает использование функций в циклах тривиальным

В ES5, если у вас есть функция внутри цикла (например, for(var i = 0; i

Обратите внимание: В данном случае нельзя использовать const, поскольку константа не может быть счетчиком цикла.

Переменные

LET

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

Это поможет сделать код более чистым, в результате уменьшения количества висящих переменных в области видимости. Например, классическая итерация массива:

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

CONST

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

Обратите внимание, что вы все еще можете изменять значения свойств объекта или членов массива:

Строки

Методы

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

Просто, но эффективно. Другой удобный метод был добавлен для создания повторяющейся строки:

Шаблонные литералы

Шаблонные литералы обеспечивают чистый способ для создания строк и интерполяции выражений. Возможно, вы уже знакомы с синтаксисом; он основан на знаке доллара и фигурных скобках . Шаблонные литералы заключаются в обратные кавычки. Краткий пример:

Как видно из кода выше, шаблонные литералы просто удобны для конкатенации по сравнению с ES5. Однако шаблонные литералы также можно использовать и для многострочных литералов. Имейте в виду, что пробельные символы (например, символ новой строки) являются частью строки:

Классы

Концептуально, в JavaScript нет такого понятия как класс, в отличие от объектно-ориентированных языков программирования, таких как Java. Но разработчики долгое время использовали функции, которые создавали объекты при вызове с ключевым словом new.

И поскольку JS не поддерживал классы, а лишь их эмуляцию с помощью прототипов, такой синтаксис вызывал сложности и у опытных JS-разработчиков, и у новичков, желающих использовать традиционные концепции OOP. Это особенно актуально для таких вещей как: наследование, вызов метода родительского класса и т.д.

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

Classes

ES6 introduced classes.

A class is a type of function, but instead of using the keyword
to initiate it, we use the keyword
, and the properties are assigned inside a
method.

Use the keyword to create a class, and
always add a constructor method.

The constructor method is called each time the class object is initialized.

Example

A simple class definition for a class named «Car»:

class Car { 
constructor(brand) {    this.carname = brand; 
}}

Now you can create objects using the Car class:

Example

Create an object called «mycar» based on the Car class:

class Car { 
constructor(brand) {    this.carname = brand; 
}}mycar = new Car(«Ford»);

Learn more about classes in our JavaScript Classes chapter.

JS Уроки

JS HOMEJS IntroductionJS Where ToJS OutputJS StatementsJS SyntaxJS CommentsJS VariablesJS OperatorsJS ArithmeticJS AssignmentJS Data TypesJS FunctionsJS ObjectsJS ScopeJS EventsJS StringsJS String MethodsJS NumbersJS Number MethodsJS ArraysJS Array MethodsJS Array SortJS Array IterationJS DatesJS Date FormatsJS Date Get MethodsJS Date Set MethodsJS MathJS RandomJS BooleansJS ComparisonsJS ConditionsJS SwitchJS Loop ForJS Loop WhileJS BreakJS Type ConversionJS BitwiseJS RegExpJS ErrorsJS DebuggingJS HoistingJS Strict ModeJS this KeywordJS Style GuideJS Best PracticesJS MistakesJS PerformanceJS Reserved WordsJS VersionsJS Version ES5JS Version ES6JS JSON

Arrow Functions

Arrow functions allows a short syntax for writing function expressions.

You don’t need the keyword, the keyword, and the
curly brackets.

// ES5
var x = function(x, y) {
  
return x * y;
}
// ES6
const x = (x, y) => x * y;

Arrow functions do not have their own .
They are not well suited for defining object methods.

Arrow functions are not hoisted. They must be defined before they are used.

Using
is safer than using , because a function expression is
always constant value.

You can only omit the keyword and the curly brackets if the function is a single statement.
Because of this, it might be a good habit to always keep them:

Оператор распространения

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

Во-первых, посмотрим, как развернуть элементы массива внутри другого массива:

Синтаксис распространения также удобен при вызове функций с аргументами:

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

Мы применяем оператор распространения к массивам и аргументам. На самом деле, он может быть применен ко всем итерируемым объектам, таким как :

Теперь – это одномерный массив, содержащий элементы и их дочерние элементы и .

Классы

Итак, выше вы уже достаточно насмотрелись примеров конструкторов, свойств и методов, которыми мы пользовались до выхода в свет нового стандарта. Не буду вас больше томить ожиданием и сразу перейдём к коду:

Пример выше можно записать в стиле ES5 следующим образом:

Что нужно знать про классы:

  • Создавая класс, вы пользуетесь блоком кода (всё, что находится между и ), внутри которого объявляете, всё, что хотите видеть в прототипе.
  • Запись означает, что будет создана функция конструктор (всё точно так же, как и в ES5)
  • Свойство используется для обозначение того, что будет происходить непосредственно в самом конструкторе.
  • Все методы для класса используют краткую запись, которую мы обсуждали ранее в статье про расширение литерала объектов.
  • При перечислении методов не надо использовать запятые (на самом деле, они запрещены)

Важно понимать, что мы до сих пор работаем с обычными функциями. То есть если вы захотите проверить тип класса, то (с удивлением?) обнаружите :. Как и при работе с функциями конструкторами мы можем записать “класс” (на самом деле, только конструктор) в переменную

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

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

Но, как и во всех остальных вопросах в JavaScript, здесь есть свои подводные камни. Во-первых функция обязана быть вызвана с оператором и, во-вторых, в то время, как функция поднималась наверх (стандартный hoisting) и её можно было использовать в любой части кода, конструктор, созданный с помощью не испытывает на себе поднятия.

extends

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

super

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

Но это ещё не всё, чем может порадовать ! Если вы захотите обратиться к любому методу, записанному в прототип родителя внутри метода потомка, то и здесь вас сможет выручить.

Без нам бы пришлось напрямую обращаться к прототипу конструктора родителя, чтобы получить перезаписанный нами метод:

Таким образом, “оценивает ситуацию” и в зависимости от того, где вы его решили использовать будет работать по-разному, но при любом использовании он готов достаточно сильно сократить ваш код и избавить от не самого понятного способа вызова конструктора родителя.

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

Классы без конструкторов

Как вы, наверное, заметили в примере выше не было использовано свойство для класса , поскольку в нём просто нет необходимости в данном случае. Но свойство всё равно было записано. Магия? Магия! Когда вы не указываете явно, что нужно сделать в конструкторе, то всё решается без вашего участия. Можно представить себе данный процесс следующим образом:

static

При работе с конструкторами в ES5 многие привыкли использовать функции, как объекты (они же и есть объекты) и вешать на них служебные функции:

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

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

Classes

ES6 introduced classes.

A class is a type of function, but instead of using the keyword
to initiate it, we use the keyword
, and the properties are assigned inside a
method.

Use the keyword to create a class, and
always add a constructor method.

The constructor method is called each time the class object is initialized.

Example

A simple class definition for a class named «Car»:

class Car { 
constructor(brand) {    this.carname = brand; 
}}

Now you can create objects using the Car class:

Example

Create an object called «mycar» based on the Car class:

class Car { 
constructor(brand) {    this.carname = brand; 
}}mycar = new Car(«Ford»);

Learn more about classes in our JavaScript Classes chapter.

# Деструктуризация

Одним из наиболее распространенных шаблонов, возникших в современном JavaScript коде является использование вариантных объектов.

Если принять, что API принимает обычные аргументы, а не объект с параметрами, то вызов fetch превращается в задачу запоминания порядка аргументов и ввода ключевого слова null в нужное место.

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

Обычно это сопровождается ручной установкой значений по-умолчанию локальным переменным:

И к сожалению для нас, несмотря на свою распространенность, практика использования || фактически привносит трудно выявляемые ошибки. Например, в этом случае мы не допускаем того, что opts.body может быть , поэтому надежный код скорее всего будет выглядеть так:

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

Собственно, значение по умолчанию можно применить и ко всему объекту с параметрами:

Вы также можете деструктурировать оператор присваивания:

Это напоминает мне о выразительности, предоставленные with, но без магии или негативных последствий.

Ссылка на основную публикацию