Skip to content

Commit

Permalink
add types & grammar lecture notes
Browse files Browse the repository at this point in the history
  • Loading branch information
AMashoshyna committed Apr 15, 2020
1 parent e311020 commit e5438d7
Showing 1 changed file with 121 additions and 0 deletions.
121 changes: 121 additions & 0 deletions types_grammar/lecture_notes.md
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
```

0 comments on commit e5438d7

Please sign in to comment.