# 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]; inameIvansurnameIvanovvfatherNamePetrovich ## Функции В семантике **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()); ```