# Javascript для Jav-истов
# JAVA !== JAVASCRIPT && JAVA != JAVASCRIPT
## Отладка
Вместо IDE есть **Developer Tools** В браузере.
**Developer Tools** содержит в себе множество средств отладки кода. Для хорошего понимания управляющих конструкций (**условий** и **циклов**) полезно
разобраться с **пошаговой отладкой** - выполнением кода по шагам. Это позволяет увидеть, какие блоки кода выполняются или не выполняются в условных
операторах и каковы значения переменных в каждый момент времени выполнения кода.
Для начала пошаговой отладки устанавливается **точка останова** - строка кода, с которой обычный режим выполнения прерывается и начинается отладка по шагам.
В **Developer Tools** найдите вкладку *Source*, в ней свой файл c кодом, и кликните на номере нужной строки слева от текста кода.
Если вы используете [repl.it](http://repl.it), другие **онлайн-IDE** или **console**, то у вас будут определенные сложности с нахождением
вашего кода и строки в нём. Поэтому вы можете вставить ключевое слово `debugger` в ваш код - это работает так же, как **точка останова** на
строке в Developer Tools.
### Отладка по шагам.
**Пошаговая отладка** позволяет детально заглянуть в процесс выполнения вашего кода - вы можете узнать всё ли работает так, как нужно, в любой строке и таким
образом упростить поиск логических ошибок в коде. Основные операции:
- **Step over next function call** (**F10**) - следующий шаг/оператор в вашем коде. После выполнения каждой команды вы можете ознакомится со значениями
переменных, наведя на них курсор мыши, написав их в консоли, или же используя вкладку Watch
- **Resume script execution** (**F8**) - переход из режима отладки по шагам в обычный режим выполнения кода. Таким способом вы пропускаете хорошо отлаженные
части кода. Этот режим может быть прерван следующей **точкой останова** или ключевым словом `debugger` в коде.
- **Step into next function call** (**F11**) - сделать следующий шаг *в функцию*, "зайти" в неё.
- **Step out of current function** (**Shift-F11**) - следующий шаг *из* функции, выйти из функции на уровень, который функцию вызвал.
### Значение переменных и выражений
- Вы можете узнать значение переменных, наведя на них курсор мыши
- Вы можете узнать значение синтаксически верного выражения выделив его и наведя на него курсор мыши. Учтите, что при этом может быть вызвана
та или иная функция и могут возникнуть побочные эффекты.
### Пошаговая отладка и консоль.
Очень удобно использовать консоль и пошаговую отладку одновременно. Консоль предоставляет все переменные и их текущие значения, которые сейчас есть в
отлаживаемом коде, таким образом вы можете поэкспериментировать с этими значениями и, например, запустить из консоли следующий проблемный оператор в коде
или его часть для нахождения логической ошибки.
### Отладочный вывод
Вы всегда можете добавить `console.log` в место, где хотите получить информацию о состоянии программы. Этот способ хорош как дополнение к остальным.
Так же вы можете написать определенное условие для отладки, вставить в него `console.log` и поставить **точку останова**.
### Примеры ниже
...могут быть запущены в отладочном режиме по красной кнопке :-), если вы откроете **Developer Tools**. Без **Developer Tools** они тоже работают.
## Подключение
"Корнем" **HTML**-документа является файл **html**, в котором указываются остальные части страницы - картинки, css, js файлы и код.
### Подключение отдельных файлов и библиотек
```html
```
### Подключение кода inline.
```html
```
## `use strict`
Javascript-интерпретатор в браузере может работать в двух режимах: "обычном" и в "строгом":
- **Обычный** режим предназначен для совместимости.
- **Строгий** режим включает *современный* стандарт **Javascript** (ECMAScript 5 или новее)
В зависимости от режима поведение интерпретатора может
меняться. Далее это будет упоминаться в тех или иных моментах. По умолчанию интерпретатор работает в обычном режиме, для включение строгого режима
используется *строка* `'use strict'` в начале **Javascript**-кода.
```javascript
"use strict";
```
или
```javascript
'use strict';
```
## Семантика и её отличия от **Java**.
- В **Java** код может исполняться многопоточно (однако каждый поток работает изолированно) и **синхронно**, т. е. код выполняется в том порядке, в котором он написан;
в коде никаких фоновых и
непоследовательных операций обычно не предусмотрено, ввод-вывод (запись и чтение файлов, сетевые запросы, запросы к СУБД) происходят в **блокирующем**
режиме, т. е. Java-код останавливает выполнение и ожидает отправки или приема той или иной порции данных.
- В **JS** код исполняется однопоточно (за редким исключением), и **асинхронно**, т. е. код представляет из себя не единую последовательность действий,
а набор функций, которые могут исполнятся в произвольном порядке в качестве обработчиков событий на странице, событий, связанных с временными задержками
или сетевых событий. Однако надо учитывать, что код, расположенный в функции, обычно работает **синхронно** и **однопоточно**, *никакой другой обработчик*
события не может работать, пока работает другой код. **Асинхронными являются только вызовы обработчиков событий из движка браузера**, когда же вы вызываете
функцию в своем коде, весь ваш код работает *синхронно*. Ввод-вывод так же происходит в асинхронной манере - вместо ожидания данных, интерпретатор
освобождается для обработки других событий; по окончанию операции ввода-вывода вызывается соответствующая функция, обрабатывающая результаты.
Данные отличия приводят к сложности написания, отладки и понимания **JS** кода. Стандартные ошибки:
- Путание определения функции и её выполнения.
- Ожидание работы кода в последовательности написания, а не в последовательности событий, которые запускают те или иные части кода.
### Семантические последствия и особенности **JS**.
- Язык унаследовал некоторые черты функциональных языков и языков высокого уровня абстракции (`Scheme`, `Common Lisp`)
- Функциональный подход превалирует над "обычным" ООП-подходом, привычным после **PHP**/**C++**/**Java**.
- Функции являются объектами первого класса, т. е. могут быть сохранены как переменные и переданы/возвращены в/из других функций.
- Объект и ассоциативный массив - это одно и то же.
- В ассоциативных массивах наряду с другими типами данных можно хранить функции, что превращает массивы в объекты. (**инкапсуляция**)
- Функции можно заносить в объекты, запускать как методы, или как отдельные функции. `this` обычно равен объекту, указанному до `.`, однако
есть возможность передать функции в качестве `this` любой объект. По факту `this` - еще один специфический параметр функции.
- Можно менять и переопределять структуру *почти* всех объектов, включая стандартные, что позволяет кардинально менять их поведение.(**полиморфизм**)
Таким образом можно сделать аналог примесей, интерфейсов и множественного наследования.
- В **JS** реализовано так называемое прототипное ООП: вместо указания предка и обработки наследования на уровне интерпретатора, в **JS** в объекте
есть ключ `prototype`, который является ссылкой на другой объект-прототип. **В случае, когда тот или иной ключ (имя метода или поля) в объекте не найден,
JS ищет ключ в объекте-прототипе**, и далее по цепочке прототипов. Таким образом реализуется **наследование**.
- `private`, `protected` и `public` нет.
- Функция исполняется одновременно в *динамическом* (значение параметров при вызове) и *лексическом* (переменные из областей видимости *декларации* функции)
контекстах. Таким образом реализуются **замыкания**: переменные области видимости *завершенной* функции сохраняются для использования функциями,
определенными внутри, *в случае* если внутренние функции были переданы "наружу" как результат работы функции или другими способами остались существовать
после окончания внешней функции. Таким образом реализуются аналог `private` полей объекта, а так же сохраняется контекст для отложенных операций или
обработчиков событий. **Замыкания** являются *объектами* в общем смысле этого слова, так как инкапсулируют код и данные.
- Синтаксис схож с **Java**, но это обманчивое сходство.
- Язык с **нестрогой динамической типизацией**, т. е. переменные типа *не имеют*, однако тип имеют *значения* в этих переменных.
- Структуры резиновые и могут быть изменены в любой момент
### Типы данных
- Целые и дробные числа представлены едиными типом `Number`
- Под "массивом" в **JS** подразумевается массив с целочисленными ключами.
- Ассоциативные массивы и объекты - это одно и то же;
- Для переменных без значения и значений отсутствующих ключей в ассоциативных массивах вместо `NULL` используется аналогичный тип `undefined`
- Так же существует тип `null`, который применяется программистами для задания "пустых" значений и/или в **DOM**.
## `;`
В отличие от **Java** и многих других языков с **C**-подобным синтаксисом, в **JS** точка с запятой *не является обязательной*, однако нужна
в некоторых случаях, например при написании операторов в одну строку:
```javascript
var a = 5
var b = 6
var c = "string"
var d
a ++; b += a; d = c + a;
```
**Во избежание ошибок просто добавляйте `;` "как обычно", в конце строки**
## Комментарии
Как в Java.
## Строки, переменные в них и конкатенация
```javascript
alert(`случайное число ${Math.random()}`)
alert('ОДинарные кавычки ' + Math.random())
alert("Двойные ничем не отличаются " + Math.random())
```
Строки иммутабельны, как в **Java** в них есть методы.
## `+` и динамическая типизация.
Так как в **JS** нет отдельного оператора конкатенации, то `+` между числами в строках может вас удивить:
![суть](https://pp.vk.me/c631923/v631923540/45dbe/uK3mUei6G5s.jpg)
Для того что бы избежать подобных ситуаций, приводите числа в строках перед использованием в математических операциях:
- `+"123"`. Простой и краткий способ для приведения строки к числу
- `parseInt("123")` или `parseFloat("123.45")` работает схожим образом, однако обладает другими странностями и возможностями (например есть возможность
задать систему счисления)
## Операторы, условия, циклы.
### Вызов функций
Большинство кода состоит из тех или иных вызовов функций. В **JS** они выглядят почти так же:
```javascript
alert("as in Java");
alert("as in JS, without semicolon")
```
Во второй строке примера нет `;`, в этом невеликое отличие.
### Операторы
В большинстве своем повторяют обычный набор **PHP**, **C**, **Java** и так далее (`+`, `-`, `*`, `/`, `%`, `++`, `--`, `+=` ...).
### `&&` и `||`.
В отличие от **Java**, в **JS** **И** и **ИЛИ** возвращают *оригинальное значение операнда*, а не булевский результат логического оператора.
#### `||`
Результат `||` становится очевиден после первого же значения, интерпретируемого как `true`. **ИЛИ** *слева направо* поочередно приводит операнды к Boolean
и возвращает *первый* операнд, *интерпретируемый* как `true`. Если такого операнда нет, **ИЛИ** возвращает последний (правый) операнд. **Результатом**
оператора является не приведенное к типу Boolean значение, а *оригинальное значение операнда*
#### `&&`
Результат `&&` становится очевиден после первого же значения, интепретируемого как `false`. **И** *слева направо* поочередно приводит операнды к Boolean
и возвращает *первый* операнд, *интерпретируемый* как `false`. Если такого операнда нет, **И** возврщаает последний (правый) операнд. **Результатом**
оператора является не приведенное к типу Boolean значение, а *оригинальное значение операнда*
#### Приведение к типу `Boolean`
Как `false` интерпретируются:
- `false`
- `0` // 0 как число
- `""` //пустая строка
- `null`
- `undefined`
- `NaN`
Как `true` интерпретируются все остальные значения, в том числе:
- `Infinity`
- `-Infinity`
- `"0"` //строка не пуста. однако +"0" уже 0 как число, а значит false
- `{}` //пустой объект - всё равно `true`
- `[]` //пустой массив `[] == false`, но в остальных случаях работает как true
**Для проверки используйте** `!!`, то есть двойное отрицание: `!!null` равен `false`, таким образом мы можем почти всегда проверить как интерпретируется
то или иное значение.
В общем случае объект является `true`, за исключением `null` и `[] == false`
**Примеры**
```javascript
2
1+1
2*1
// bool type cast
!!2
!!0
!!1
// or
2 || 1
2 || 0
//and
2 && 1
1 && 2
0 && 2
// or and and difference
0 || 1 || 2
0 && 1 && 2
2 || 1 || 0
2 && 1 && 0
//null, undefined, so on
null || 2
undefined && 1
//brackets and complex expressions
(undefined || 2) && (2 || 0)
(2 && 1) || (null && 0)
(2 > 1) && "greater"
(2 < 1) && null
null && (2 < 1)
// ternary operator
1 ? "one" : "not one"
0 ? "zero" : "not zero"
"0" ? "\"zero\"" : "not `zero`"
parseInt("0") ? 'true' : 'false'
("" || 2) && (3 || "3.5") || (4 && 5)
(-1 + 1) && "zero"
"-1" + 1 && "oups"
(typeof null === 'object') ? "null is object" : "null is null"
// ternary && ||
Math.random() < 0.5 && 'less' || 'more'
(a = Math.random()) < 0.5 && 'less: '+a || 'more: '+a
//in for array
[2,3,5,7,11].indexOf(7) > -1 ? 'prime' : 'not found'
'random' in Math
'indexOf' in []
var a = b = c = d = 5;
```
**Java** ведет себя по-другому:
```php
2 || "aaa";// bool(true)
0 && "aaa";// bool(false)
```
### Условия `if` и `switch`.
Работают аналогично **Java**.
### Циклы
#### `for ... in`
```javascript
var car = {brand: "Lada",
'model': "2101"};
for (var key in car){
alert(key + ": " + car[key]);
}
```
Как можно заметить, при этом в цикле нет переменной со значенинем, а только с ключем, по которому можно в цикле получить значение из итерируемого массива.
#### `for`, `while` и `do-while`
работают так же как в **Java**:
```javascript
for (var i=0;i<10;i++){
console.log(i);
}
```
## Типы данных и Объектная модель.
### Обращение к полям и методам объектов **JS**
Как и в **Java**, через точку `.`.
### Числа.
Кроме того, что целые и дробные числа объединены в единый тип `Number`, особых отличий с **Java** нет:
```javascript
console.log(1/0); //Infinity
console.log(-1/0);//-Infinity
console.log(1/"asdf");//NaN
```
Приведение к числу из строки:
```javascript
console.log(+"aaa100500"); //NaN
console.log(+"100500"); //100500
```
Также **JS** не выбрасывает исключение "Деление на ноль", а просто возвращает бесконечность.
#### Число как объект
С числами можно работать как с объектами:
```javascript
console.log(5.123456.toFixed(2)); // "5.12"
```
Тем не менее, многие математические операции вынесены в глобальный объект-коллекцию `Math` (например, Math.round, Math.ceil, Math.random и другие)
### Строки
Большинство строковых операций являются методами объекта-строки:
```javascript
console.log("12345".length); //5
console.log("aBcDe".indexOf("D")); // 3
console.log("123string456".substr(3,6)); // "string"
```
### Boolean
Работает как в **Java**
### `undefined`
Аналог `null` в **Java**. В этом типе есть только одно значение - `undefined`
### `null`
`null` - это "`undefined` для программиста", а не для интерпретатора. Так же используется в **DOM** для пустых/незаданных значений.
### Массивы
Под массивом подразумевается нумерованный целочисленными индексами массив. Массив резиновый и нетипизированный.
```javascript
var a1 = [1,2,3]
var a2 = new Array(4,5,6)
alert(a1[1]) //
a1.push(5)
alert(a1.indexOf(3)) //
alert(a2.length) //
//Можно добавить именованное поле в обычный массив как и в любой другой объект JS, но...
a2.someKey = 15;
a1["someOtherKey"] = 25;
//от этого они не появятся среди других нумерованных элементов
alert(a2);
alert(a1);
//но будут доступны наряду со стандартными полями объекта Array (length, indexOf и другие)
alert(a2["someKey"]);
alert(a1.someOtherKey);
```
Посему используйте для массивов типа `Array` только целочисленные ключи.
### Ассоциативные массивы.
В **JS** ассоциативные массивы и объекты - это *одно и тоже*. Так же любой объект, даже встроенный, может быть изменен в любой момент.
```javascript
var person = {
name: "Ivan",
surname: "Ivanovv",
"fatherName": "Petrovich",
}
typeof person
```
Нет разницы, определять ключи *литерально* или через строку (в кавычках `"fatherName"`).
```javascript
person.fatherName
person["name"]
```
Обратите внимание, что `person.fatherName` работает так же как и `person["name"]`, несмотря на то, что определены наоборот.
Для обращения через ключ в переменной используется нотация с квадратными скобками:
```javascript
var key = "surname";
person[key]
person.key
```
Если просто написать `person.key`, то **JavaScript** будет искать ключ `key` *литерально*, а не по значению переменной `key` ("surname")
Вы можете определить новый элемент массива просто присвоив ему значение:
```javascript
person.age = 98;
person
```
Также можно создавать массив через конструктор Object:
```javascript
var a = new Object();
a.name = "Petr"
a.surname = "Petrov";
a["age"] = 17;
```
Получить ключи ассоциативного массива можно с помощью функции `Object.keys`:
```javascript
Object.keys(person)
```
В качестве значений в ассоциативном массиве могут быть любые типы данных, в том числе и другие ассоциативные массивы:
```javascript
var tree = {
name: "Ivan",
fatherName: "Petrovich",
surname: "Ivanov",
children: [
{
name: "Maria",
fatherName: "Ivanovna",
surname: "Ivanova",
},
{
name: "Petr",
fatherName: "Ivanovich",
surname: "Ivanov",
},
{
name: "Nikolay",
fatherName: "Ivanovich",
surname: "Ivanov",
},
]
}
```
Нет сложностей с циклическими ссылками:
```javascript
var a = {}
var b = {}
a.b = b
b.a = a
b.b = b
a.a = a
a.name = "A"
b.name = "B"
for (var i=0,child=tree.children[i]; iname | Ivan |
---|
surname | Ivanovv |
---|
fatherName | Petrovich |
## Функции
В семантике **JS** функциям отведена огромная роль:
Они могут использоваться
- Как обычные функции в процедурном стиле написания кода
- Как *объекты первого класса*, т. е. как переменные. Это позволяет их передавать в другие функции, сохранять в переменных и так далее:
- Функциональный подход (`filter`, `map`, `reduce`, `sort`)
- Функции, сохраненные в ассоциативном массиве становятся методами
- Асинхронное программирование - обработчики событий и длительных операций ввода-вывода передаются в виде функций.
- Замыкания.
**Java** тоже позволяет делать многое из этого, однако исторически такой подход редко используется в Java-коде.
### Основные синтаксические различия c **Java**
Основные синтаксические различия касаются параметров функции:
- В функциях в **JS** нет параметров по умолчанию (в ES6 - есть)
- Набор параметров при вызове функции может отличаться от набора параметров при декларации, и это не вызывает ошибок
- Вы всегда можете узнать актуальный набор параметров в форме псевдомассива `arguments`.
```javascript
function add2(a,b){
if (typeof a === 'undefined'){
a = 0;
}
b = b || 0;
return a + b;
}
alert(add2());
alert(add2(5));
alert(add2(5,7));
```
```javascript
function add(){
var result = 0;
for (var i=0;i randomValue
}
alert(getRandomValue())
```
### makeAdder
```javascript
function makeAdder(x){
function adder(y){
return x + y;
}
return adder
}
var add5 = makeAdder(5);
var greeter = makeAdder("Hi, ");
alert(add5(2));
alert(greeter("John"));
```
В примере выше `5`,`"Hi, "`, `2` и `"John"` находятся в динамическом контексте вызова функции. А вот значение `x` в `adder` находится в *лексическом*
контексте. На момент запуска функций `add5` и `greeter` функции `makeAdder` уже давно отработали, однако значение `x` и область видимости продолжают
присутствовать в функциях `add5` и `greeter`. Это называется **замыканием** - данные в локальной области видимости отработавшей функции и код, который
использует эти данные впоследствии. В широком смысле **замыкание** является *объектом*, так как хранит в единой сущности код и данные.
В контексте **JS** *замыкания* очень удобны для огораживания кода, сохранения состояния переменных "на будущее", для функций обратного вызова. Так же
*замыкания* удобны для организации приватных полей у объектов **JS**.
```javascript
function makeCounter(){
var counter = 0;
return function(){
return counter++;
}
}
```
Функция выше создает счетчик, значение которого можно узнать из возвращаемой анонимной функции. При этом счетчик увеличится на 1. Если расширить функционал
данного примера для чтения и декремента счетчика, получим, например:
```javascript
function makeCounter(){
var counter = 0;
function inc(){
return counter++;
}
function dec(){
return counter--;
}
function read(){
return counter;
}
return [inc,dec,read];
}
```
Результатом выполнения `makeCounter` будет *массив функций*. Однако намного более наглядным будет создание именованного массива, т. е. объекта:
```javascript
function makeCounter(){
var counter = 0;
function inc(){
return counter++;
}
function dec(){
return counter--;
}
function read(){
return counter;
}
return {inc: inc, dec: dec, read: read};
}
```
... что во многом схоже с обычным объектом сделанным через **функцию-конструктор**:
```javascript
function Person(name, surname){
this.name = name
this.surname = surname
var age = 0
function checkNewAge(){
return (!isNaN(this.newAge) && Number.isInteger(this.newAge) && (this.newAge > 0) && (this.newAge < 100))
}
this.setAge = function(newAge){
this.newAge = +newAge
if (checkNewAge()){
age = this.newAge;
}
return age
}
this.getAge = function(){
return age
}
this.getOriginalName = function(){
return name;
}
this.getOriginalSurname = function(){
return surname;
}
}
var person = new Person("Ivan", "Petroff")
alert(person.setAge(50))
alert(person.setAge(125))
alert(person.setAge(25))
person.name = "John";
person.surname = "Doe";
alert(person.getOriginalName());
alert(person.getOriginalSurname());
```