TypeScript: объявление переменных

Назад | Учебник TypeScript | Вперёд

let и const — это два новых способа объявления переменных в JavaScript. С помощью  let мы объявляем переменные, а с помощью const мы объявляем константы.

Объявление переменных с помощью var

В Javascript переменные традиционно объявлялись с помощью ключевого слова var.

Этим кодом мы только что объявили переменную a и присвоили ей начальное значение 10.

Мы также можем объявлять переменные внутри функций:

Вы также можете обратиться к переменной, объявленной во внешней функции:

В этом примере выше g захватывает переменную a, объявленную в f. В любой точке вызова g значение переменной a будет связано со значением переменной a в f. Даже если функция g вызвана после завершения работы функции f, то всё равно будет возможно получить доступ к переменной a  и изменить её.

Область видимости объявлений с var

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

Обратите внимание, что переменная x была объявлена внутри блока if, но мы всё равно можем к ней обратиться снаружи этого блока.

В JavaScript объявления переменных с помощью var доступны везде внутри содержащей их функции, модуля, пространства имён или глобального пространства. Областью видимости переменных, объявленных с var в JavaScript являются функции. Именно поэтому в TypeScript их область действия такая же.

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

В приведённом примере вложенный for случайно использует (пытаясь её объявить, правда) переменную i внешнего цикла, но областью действия переменной, объявленной с var будет функция, а не блок for.

Причуды захвата переменных

Попробуйте угадать, каков будет результат кода:

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

А теперь посмотрите на результат:

Многие JavaScript-разработчики незнакомы с подобным поведением. Многие ожидают такой вывод:

Вспомните, что мы упоминали про захват переменных? Каждое функциональное выражение, которое мы передаём в setTimeout  обращается к той же самой переменной i в той же самой области видимости.

Давайте разберём, что на самом деле происходит. Функция setTimeout  запускает переданную функцию после указанного количества миллисекунд, но только после окончания цикла for. По окончании цикла for значение переменной i будет равно 10, поэтому при каждом вызове функции она будет писать 10!

Объявление переменных с помощью let

Из-за описанных выше проблем объявления переменных с помощью var был введён новый способ объявления переменных — с помощью let.

Основное отличие объявления переменных с помощью let — это то, что областью видимости переменных становится блок, в котором они объявлены. В отличие от переменных объявленных с помощь var, переменные, объявленные с помощью let, не видны снаружи блока или цикла for, в котором они объявлены.

Переменные, объявленные в блоке catch, имеют такую же область видимости:

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

Однако вы всё ещё можете захватить (capture) такую переменную до её объявления. Однако использовать такую функцию до объявления этой переменной нельзя. Для ES2015 будет бросаться исключения, однако сейчас TypeScript не сообщает о такой ошибке.

Повторное объявление и затенение

Для var не имеет значения количество обращений переменных, сколько бы вы не объявляли переменная будет только одна.

В примере выше все объявления на самом деле указывают на одну и ту же x, это вполне допустимо. Это часто становится источником ошибок. Однако объявление переменных с помощью let позволяет избежать этого:

Переменные не обязаны быть обе объявлены с помощью let, чтобы TypeScript сообщил нам о проблеме:

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

Объявление новой переменной с тем же именем  во внутреннем блоке скрывает от нас переменную внешнего блока. Это называется затенением.

Затенение — это «меч с двумя лезвиями». Оно может добавить новых ошибок в случае случайного использования, но также может предотвратить некоторые ошибки. Например, представьте, что мы переписали написанную ранее функцию sumMatrix с помощью переменных, объявленных с let.

Эта версия цикла суммирует правильно, так как переменная i  внутреннего цикла затеняет переменную i внешнего цикла.

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

Захват переменных с блочной областью видимости

Когда мы впервые коснулись идеи захвата переменных с объявлением var, мы коротко прошлись по их поведению после захвата. Каждый раз при заходе в новую область видимости создаётся новая «среда» для переменных. Эта среда и захваченные переменные могут существовать даже после окончания выполнения кода внутри области видимости.

Так как мы захватили city в её области видимости, мы всё ещё можем к ней обратиться, не смотря на то, что блок if закончил своё выполнения.

Вспомните наш пример с setTimeout, где мы использовали IIFE для захвата значений переменных для каждой итерации for. В результаты мы создавали новую среду переменных для захваченных переменных. Это доставило немного боли, но, к счастью, нам никогда не придётся делать это с TypeScript.

Объявления переменных с let имеют совершенно другое поведения при объявлении в части цикла. Вместо создания новой среды для всего цикла они создают как-бы новую область для каждой итерации, что избавляет нас от необходимости использования IIFE:

Результат:

Объявления const

Объявления с помощью const работают так же, как и let, но не позволяют менять значения своих переменных после присвоения.

Нельзя менять именно значения самих переменных, объявленных с помощью const, но значения, на которые они ссылаются вполне могут менять своё внутреннее состояние:

Деструктурирование

Это НЕ уничтожение.

Деструктурирование массивов

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

Это создаёт две новые переменные: first и second . Это эквивалентно обращению по индексу, но более удобно:

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

И с параметрами функций:

Вы можете создать переменную для оставшихся элементов в списке с помощью ...:

Разумеется, так как это JavaScript, то вы можете просто игнорировать элементы в конце, которые вам не важны:

Или другие элементы:

Деструктурирование объектов

Вы также можете деструктурировать объекты:

Это создаст новые переменные a и b из o.a и o.b. Обратите внимание, что вы можете пропустить c, если она вам не нужна.

Так же как и с массивами можно делать присвоения без объявления:

Обратите внимание, что мы использовали круглые скобки, так как JavaScript обычно рассматривает «{» как начало блока.

Вы можете создать переменную для оставшихся элементов в объекте с помощью ...:

Переименование свойств

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

Этот синтаксис может привести в замешательство. Вы можете читать a: newName1 как « a as newName1». Направление чтения — слева направо, как если бы вы писали:

Сбивает с толку то, что двоеточие здесь НЕ указывает тип. Тип, если вы его указываете, нужно указывать после всей конструкции:

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

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

keepWholeObject теперь содержит как переменную wholeObject, так и свойства a и b, даже если b не задавалось.

Объявление функций

Деструктуризация также работает и с объявлением функций. Например:

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

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

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

Распространение

Распространение обратно деструктурированию. Оно позволяет распространять массив в другой массив или объект в другой объект. Пример:

В результате мы получаем bothPlus  со значением [0, 1, 2, 3, 4, 5]. Распространение создаёт теневую копию first и second. Они не меняются распространением.

Вы также можете распространять объекты:

Теперь search равен { food: "rich", price: "$$", ambiance: "noisy" }. Распространение объектов более сложно, чем распространение массивов. Как и в распространении массивов оно работает слева направо, но в результате всё равно объект. Это озачает, что свойства, кторые приходят позже в распространённый объект могут перезаписать свойства, пришедшие раньше. Если мы изменим предыдущий пример к распространению в конце:

Тогда свойство food в defaults перезапишет food: "rich", что не совсем то, что мы хотим в этом случае.

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

Во-вторых компилятор TypeScript не позволяет распространять обобщённые параметры в обобщённых функциях. Эта возможность ожидается в будущих реализациях языка.

Назад | Учебник TypeScript | Вперёд

 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *