-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e311020
commit e5438d7
Showing
1 changed file
with
121 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
|
||
# Система типов | ||
|
||
Система типов языка - это набор правил, по которым всякому значению присваивается определенный тип. Тип, присвоенный значению, сообщает компилятору или интерпретатору, сколько памяти нужно выделить для этого значения, а также какие операции над ним можно производить. Задача системы типов - сократить количество ошибок, вызванных тем, что над неким значением проводится недопустимая операция. Примеры таких ошибок: | ||
|
||
```javascript | ||
// Вызов сущности, которая не определена или не является функцией | ||
const myObj = { bar: 42 }; | ||
myObj.foo(); // TypeError: myObj.foo is not a function | ||
myObj.bar(); // TypeError: myObj.bar is not a function | ||
|
||
// Обращение к свойству сущности, не являющейся объектом (в примере - undefined) | ||
console.log(myObj.foo.bar); // TypeError: Cannot read property 'bar' of undefined | ||
|
||
// Недопустимые операции над сущностями разных типов | ||
42 - 'foo'; // NaN | ||
|
||
``` | ||
|
||
Говоря о системе типов в языке, часто противопоставляют строго и нестрого типизированные языки, а также статически и динамически типизированные. Это смежные понятия, но не одно и то же. | ||
Работа системы типов состоит из двух частей - присвоения типа значению и проверки согласованности типов в программе. Статическая либо динамическая типизация определяется моментом, в который типы присваиваются значениям и проверяются (во время компиляции либо во время исполнения программы). Также при статической типизации тип присваивается переменной, а при динамической - значению (поэтому в одну и ту же переменную могут быть записаны значения разные типов). | ||
|
||
Сильная или слабая типизиция - это характеристика того, насколько сложно обойти систему типов языка и "выстрелить себе в ногу". | ||
|
||
Когда говорят о сильно типизированных языках, действительно чаще всего подразумевают языки статически типизированные, но важно понимать, что это не одно и то же. | ||
|
||
# Типы в JavaScript | ||
|
||
В JavaScript по состоянию на 2020 год есть 8 типов: | ||
- String | ||
- Number | ||
- Boolean | ||
- Object | ||
- null | ||
- undefined | ||
- Symbol | ||
- BigInt | ||
|
||
Все типы делятся на две большие группы: примитивные (все остальные, кроме Object) непримитивные типы (единственный тип - Object). | ||
Все примитивные типы иммутабельны и не имеют свойств. | ||
|
||
# Оператор typeof | ||
|
||
Оператор typeof возвращает тип переданного значения в виде строки. Он может вернуть одно из 8 значение, но обратите внимание, что эти значения не совпадают с набором типов из предыдущего раздела: | ||
string => 'string' | ||
number => 'number' | ||
boolean => 'boolean' | ||
object => 'object' | ||
null => **'object'** | ||
undefined => 'undefined' | ||
symbol => 'symbol' | ||
BigInt => 'bigint' | ||
=> **'function'** | ||
|
||
Таким образом, для типов `Object` и `null` оператор `typeof` вернет значение "object", а для функций, которые являются объектами с точки зрения типов, `typeof` вернет значение "function" (так определено в спецификации языка. Обращаем внимание, что для массивов, которые тоже являются частным случаем объектов, это на работает: `typeof []` вернет "object". | ||
|
||
# Объектные обертки примитивных типов | ||
|
||
Значения примитивных типов (всех, кроме объектов) не имеют свойств и методов. Однако вы могли заметить, что мы используем свойства и методы на примитивах довольно часто: | ||
|
||
```javascript | ||
"my string".split(''); | ||
|
||
(42).toFixed(2); | ||
``` | ||
Откуда они берутся? | ||
Дело в том, что, когда мы пытаемся обратиться к свойству или методу значения примитивного типа, это значение оборачивается в соответствующий объект. Это явление называется boxing: | ||
|
||
```javascript | ||
'hello'.length // примерно то же, что new String('hello').length; | ||
``` | ||
Есть обертки для таких типов: | ||
|
||
String | ||
Number | ||
BigInt | ||
Boolean | ||
Symbol | ||
|
||
Почему же при попытке получить свойство null или undefined возникает ошибка "Cannot read property X of undefined"? | ||
Для undefined и null не существует объектных оберток, поэтому обращение к свойствам undefined и null приводит к ошибке. | ||
|
||
|
||
# Как узнать тип значения? | ||
|
||
Мы уже рассмотрели оператор typeof, но иногда его возможностей недостаточно. Для всех объектов (в том числе, например, массивов или промисов) typeof вернет значение 'object'. Часто нам нужно знать более подробно, что за объект нам попался. Для этого есть другие средства: | ||
|
||
|
||
## Оператор instanceof | ||
|
||
Оператор instanceof принимает в качестве аргумента функцию-конструктор и проверяет, если ли эта функция в прототипной цепочке исследуемого объекта. | ||
|
||
```javascript | ||
const myObj = {}; | ||
myObj instanceof Object; // true | ||
|
||
const myArray = [1, 2, 3]; | ||
myArray instanceof Array; // true | ||
``` | ||
|
||
То же самое мы можем сделать с собственными классами. | ||
|
||
## Duck Typing | ||
|
||
Однако такая проверка тоже не всегда дает нужные результаты. Например, нас может вовсе не интересовать происхождение объекта от какого-то конструктора. Нам достаточно, что у него ожидаемая форма (shape), то есть, реализованы какие-то свойства или методы. Таким случаем являются промисы. До полявления промисов в стандарте EcmaScript существовали бибилиотеки, которые реализовывали спецификацию PromiseA+. Эти бибилиотеки (например, BlueBird) используются по сей день. Создаваемые ими промисы ведут себя как и нативные промисы, но не являются экземплярами класса Promise. | ||
В таких случаях мы можем прибегнуть к так называемой утиной типизации (duck typing). Название пошло от шутливого принципа утиного теста, который звучит так: Если что-то выглядит как утка, плавает как утка и крякает как утка, то это, вероятно, и есть утка. Смысл принципа в том, что можно установить факт на основе косвенных доказательств. Применяя этот принцип к объектам в JavaScripte, мы можем установить, что объект имеет определенную форму, проверив наличие нужных свойств. | ||
|
||
|
||
```javascript | ||
if(typeof someObject.then === 'function') { | ||
// можем предположить, что это промис | ||
} | ||
``` | ||
|
||
Особый случай - проверка массива. Массив является частным случаем объекта с особым поведениям для свойства `length`. Чтобы не проверять, является ли значение массивом, через instanceof (может не сработать, например, при передача массива в iframe, у которого собственный конструктор массива) или перебором нужных свойств, мы можем воспользоваться специальным статическим методом `Array.isArray`: | ||
|
||
```javascript | ||
Array.isArray([1, 2, 3]) // true | ||
Array.isArray("[1, 2, 3]") // false | ||
``` | ||
|