- Обзор ECMAScript 6, следующей версии JavaScript
- Значения параметров по умолчанию
- Именованные параметры функций
- Destructuring assignment
- Классы
- Модули
- Цикл for-of
- Arrow-функции
- Заключение
- Ссылки
- 3 чудо-приема JavaScript для ускорения разработки
- 1. Создание асинхронного контекста
- 2. Именованные параметры функции
- 3. Функциональная обертка для блоков Try/Catch
Обзор ECMAScript 6, следующей версии JavaScript
В текущей версии JavaScript присутствует функциональная область видимости. Это означает, что все переменные, объявленные c помощью ключевого слова var , будут видны в любом месте функции (даже если они объявлены внутри блока):
function f(a) < if (a < 0) < var i = 3; >console.log(i); // 3 > f(-1)
В новой версии появится ключевое слово let , которое позволит объявлять переменные с блочной областью видимости:
function f(a) < if (a < 0) < let i = 3; >console.log(i); // ReferenceError: i is not defined > f(-1)
Значения параметров по умолчанию
В функциях добавилась возможность объявлять у параметров значения по умолчанию:
function setLevel(newLevel = 0) < . >setLevel(); // newLevel = 0 setLevel(5); // newLevel = 5 setLevel(undefined); // newLevel = 0
Именованные параметры функций
В функциях также появилась возможность указывать именованные параметры:
Именованные параметры можно комбинировать с обычным (позиционными параметрами):
function foo(positional, < named1, named2 >) < . >foo(123, < named1: 'abc', named2: 'def' >) foo(123, < named2: 'def', named1: 'abc' >)
Destructuring assignment
ECMAScript 6 позволит деструктуризировать при присваивании:
let < first: f, last: l >= < first: 'Jane', last: 'Doe' >; console.log(f); // 'Jane' console.log(l); // 'Doe'
Кстати, в примере из предыдущего пункта (Именованные параметры) вы видели пример деструктуризации параметров функции.
Деструктуризация по умолчанию является refutable (не имею понятия, как это переводить). Т.е. если в объекте-источнике присваивания соответствующего поля нету, то выбрасывается ошибка:
Если же вы не хотите, чтобы ошибка генерировалась, то переменную можно объявить как irrefutable с помощью суффикса ?:
let < first: f, last?: l >= < first: 'Jane' >; // ok console.log(l); // undefined
Либо можно дать переменной значение по умолчанию:
let < first: f, last: l = 'Unknown' >= < first: 'Jane' >; // ok console.log(l); // 'Unknown'
Значение по умолчанию также срабатывает, если соответствующее поле в объекте-источнике является undefined :
Наконец, если вы хотите, чтобы все переменные были irrefutable, то можно поставить суффикс ? в конце всего шаблона присваивания:
let < foo: f >? = anything; // всегда ok
В последнем примере переменная f будет инициализирована значением undefined , если anything будет равно undefined , null или не иметь поля foo .
С помощью деструктуризации можно одной строчкой кода поменять значение двух переменных (без всяких tmp ):
Классы
В ECMAScript 6 появятся классы:
// Supertype class Person < constructor(name) < this.name = name; >describe() < return "Person called " + this.name; >> // Subtype class Employee extends Person < constructor(name, title) < super.constructor(name); this.title = title; >describe() < return super.describe() + " (" + this.title + ")"; >>
Теперь можно использовать эти классы:
let jane = new Employee("Jane", "CTO"); jane instanceof Person; // true jane instanceof Employee; // true jane.describe(); // 'Person called Jane (CTO)'
Всего того же можно было добиться с помощью прототипов:
// Supertype function Person(name) < this.name = name; >Person.prototype.describe = function () < return "Person called " + this.name; >; // Subtype function Employee(name, title) < Person.call(this, name); this.title = title; >Employee.prototype = Object.create(Person.prototype); Employee.prototype.constructor = Employee; Employee.prototype.describe = function () < return Person.prototype.describe.call(this) + " (" + this.title + ")"; >;
Как видите, классы в ECMAScript 6 — это просто синтаксический сахар над конструкторами и функциями.
Классы могут иметь статические методы:
Приватных полей и методов не будет (по крайней мере, в ECMAScript 6). Однако некоторое сокрытие данных все же появится. Через модули.
Модули
В JavaScript наконец-то появятся модули:
module Math < export function sum(x, y) < return x + y; >export var pi = 3.141593; // Не видна снаружи function internal() < . >>
import Math.; alert("2π 2π bar.js").y; // file system
Все глобальные переменные в модули являются глобальными только в этом модуле.
Возможны циклические зависимости между модулями.
Цикл for-of
Как вы знаете, цикл for-in в JavaScript итерирует по всем полям объекта (включая наследованных). Т.е. итерироваться по значениям массива можно, но опасно:
let arr = [ "blue", "green" ]; arr.notAnIndex = 123; Array.prototype.protoProp = 456; for(var x in arr) < console.log(x); // Напечатает blue, green, notAnIndex, protoProp >
В ECMAScript 6 появится цикл for-of , который решит данную проблему:
Также, возможно, в язык добавится оператор yield , с помощью которого можно легко и красиво писать кастомные итераторы.
Arrow-функции
В ECMAScript 6 появятся arrow functions:
let squares = [ 1, 2, 3 ].map(x => x * x);
Код выше эквивалентен этому:
let squares = [ 1, 2, 3 ].map(function (x) < return x * x >);
Arrow-функции немножко отличаются от обычных функций. В первую очередь тем, что в arrow-функциях this привязан к вышестоящему контексту. Т.е.
let jane = < name: "Jane", sayHello: function (friends) < friends.forEach(friend =>< console.log(this.name + " says hello to " + friend) >); > > jane.sayHello([ 'Mark', 'John' ]);
Jane says hello to Mark Jane says hello to John
says hello to Mark says hello to John
Проблема в том, что this из анонимной функции function(friend) < . >) перекрывает this из окружающего контекста. Для того, чтобы этого избежать, можно использовать старый прием с var self = this или использовать функцию bind :
Т.е. по сути своей arrow functions — опять же синтаксический сахар над существующими анонимными функциями:
- Нельзя использовать arrow-функции как конструкторы ( new (() =><>) кинет ошибку)
- Arrow-функции не могут обратиться к переменной arguments (да и незачем)
В остальном arrow-функции не отличаются от обычных функций. Они поддерживают значения по умолчанию, переменное количество параметров, операторы typeof и instanceof :
typeof () => <>; // 'function' () => <> instanceof Function; // true
Заключение
Я описал далеко не всё, что появится в новом стандарте ECMAScript 6. И очень возможно, что что-то из того, о чем я написал выше, может измениться или вообще не появиться в стандарте. Тем не менее, все, что я описал, — это не слухи. Это вещи, реально обсуждаемые комитетом TC39. И к концу этого (2013) года стандарт должен быть утвержден.
Ссылки
Большая часть информации взята из блога доктора Axel’а Rauschmayer’a, послушать которого и повидать вживую мне посчастливилось на конференции CodeFest в Новосибирске.
PS. Спасибо 2GIS за организацию конференции!
3 чудо-приема JavaScript для ускорения разработки
За последнее время, благодаря поддержке сообщества, JavaScript стал в технологической индустрии языком, девиз которого “Напиши одни раз — выполняй где угодно”. С его помощью вы можете разрабатывать почти все, что пожелаете: мобильные, настольные и веб-приложения. На JavaScript также создаются нейронные сети и программируются дроны. Что и говорить — этот язык способен на многое. А у профессионального разработчика JavaScript всегда найдется в запасе несколько особо крутых приемов, накопленных за многие годы работы.
Далее я поделюсь тремя своими любимыми и очень эффективными приемами JavaScript, которые сэкономят время разработки.
1. Создание асинхронного контекста
Обработка асинхронных событий в JavaScript — довольно сложный аспект программирования, особенно для новичков, поскольку он непосредственно связан с промисами. Проще говоря, сам разработчик управляет выполнением кода. До выхода ES6 (2015) приходилось использовать множество методов then и catch , объединенных в цепочки, и асинхронный код обрабатывался следующим образом:
promise
.then(function () < . >)
.catch(function () < . >)
.then(function () < . >)
.catch(function () < . >)
В ES6 было представлено новое и эффективное ключевое слово await . Оно позволяет просто подождать завершения любой асинхронной задачи, после чего продолжить выполнение. Все бы хорошо, но есть одно большое “но”: место await в функции async , и его нельзя задействовать в глобальной области видимости. Поэтому мы воспользуемся IIFE для имитации асинхронного контекста глобального уровня, что позволит применять await везде, где необходимо:
let data
(async () => // Это асинхронный контекст для await
const res = await fetch("https ://google.com/mcdkncjs")
data = await res.json()
>)();
console.log(data)
Благодаря IIFE вы можете создавать асинхронный контекст для обработки различных асинхронных задач.
2. Именованные параметры функции
Как правило, при определении функции у вас есть параметры, которые поочередно передаются при вызове и разделяются запятой (,). Это прекрасно работает при наличии небольшого их числа, отличной IDE или текстового редактора с технологией IntelliSense. В противном случае усложняется процесс написания функции и приходится запоминать все параметры по порядку, что довольно утомительно.
createUser(name, surname, 1542, 21, true)
Однако с помощью ES6 можно создавать функции с именованными параметрами, облегчая процесс их чтения и написания. Вместо последовательной передачи параметров передается один объект аргументов, содержащий их все, что избавляет от необходимости запоминать порядок. Кроме того, в функции можно провести деструктуризацию объекта и получить параметры по отдельности.
// Объявляем функцию, используя деструктурированные параметры.
function createUser( name,
surname,
id,
age,
isForeigner
>) < . >
// После этого передаем объект параметров
createUser( name,
surname,
id: 1542,
age: 21,
isForeigner: true
>)
С деструктурированными параметрами вызов функций становится более явным и простым.
3. Функциональная обертка для блоков Try/Catch
С ростом популярности async/await сократились случаи применения catch , вследствие чего разработчики стали задействовать блоки try/catch для обработки ошибок в асинхронных функциях. Однако новый блок означает новый уровень индентации (стиль отступов) и новый уровень сложности.
try var quote = await getQuote();
console.log(quote);
catch (error) console.error(error);
>
Блок try/catch для обработки ошибок в асинхронных функциях полностью нивелирует преимущество индентации await перед then .
Поэтому было бы лучше, имей мы функцию, которая возвращала бы ошибку или результат в случае успеха без индентации. Именно этой реализацией мы сейчас и займемся. Для этого оборачиваем блок try/catch функцией, а затем используем ее для перехвата ошибок без индентации.
Функция обработки ошибок будет асинхронной, поскольку внутри мы намерены задействовать await и принять в качестве аргумента другую асинхронную функцию. Далее в зависимости от успешного ее выполнения вернется результат или ошибка.
const handled = async (asyncFunc) => <
try <
const res = await asyncFunc();
return [null, res];
> catch (error) <
return [error, null]
>
>
Обертывание try/catch внутри функции позволяет работать с ней ее без индентации.
С помощью функции handled можно обрабатывать ошибки на том же уровне индентации, что и await . Нужно лишь обернуть асинхронную функцию посредством handled и провести деструктуризацию и ответа, и ошибки.
const [error, res] = await handled(asyncFunc)
handled возвращает и ошибку, и ответ, но в зависимости от успеха один из них — null .
Отлично! Теперь вы знаете 3 замечательных приема JavaScript, которые являются неотъемлемой частью моей ежедневной работы. Рекомендую вам также взять их на заметку. Благодарю за внимание!