JavaScript появился в 1995 в браузере Netscape Navigator.
Несмотря на название из Java взяты только основа (язык Си), некоторые стандартные классы (Object, Math, String, ...) и невозможность перегрузки операций.
Большее влияние оказало популярное в то время функциональное программирование:
* анонимные функции (лямбда-выражения);
* отсутствие типизации (переменные могут хранить значения любого типа);
* отсутствие классов (частично исправлено в последних версиях) - вместо этого предлагалось создание объектов по образцу (язык Self);
* достаточно похожим образом осуществляется контроль за порядком вычислений в асинхронных операциях (Promise является аналогом конвейера).
Каждый разработчик браузера вносил что-то своё в язык и для обеспечения [совместимости](https://learn.javascript.ru/strict-mode) и надежности рекомендуется писать `` 'use strict'; `` в первой строке кода:
```run-js
'use strict';
let x=5;
console.log(x);
```
Например, использование необъявленных переменных в этом режиме запрещено:
```run-js
'use strict';
x=5;
console.log(x);
```
Уберите первую строку, и программа будет работать без ошибок.
Переменные, базовые типы и вычисления
[Переменные](https://learn.javascript.ru/variables) объявляются с помощью ``let`` или ``const`` и являются ссылками на значения (несколько переменных могут указывать на одно значение):
``const`` означает, что переменную нельзя переустановить на другое значение, но само значение можно изменять (если оно является коллекцией значений).
Имя переменной должно содержать только буквы, цифры или символы $ и \_. Регистр имен важен.
Базовые (примитивные) [типы данных]:
number (вещественное 64-битное число), bigint, string, boolean, null, undefined, symbol (уникальный идентификатор).
```run-js
'use strict';
let n=1.5; // number
console.log(typeof n);
let m=1; // тоже number
console.log(typeof m);
let k=123456789n; // bigint
console.log(typeof k);
let s='abc'; // string
console.log(typeof s);
let b=true; // boolean
console.log(typeof b);
let p=null; // пустой объект - специальное значение
console.log(typeof null); // object
let u; // значение не присвоено
console.log(typeof u); // undefined
let z=Symbol(); // или Symbol.for('key')
console.log(typeof z); // symbol
```
В JavaScript три вида строк:
```run-js
'use strict';
console.log('строка1\nстрока2');
console.log("строка в \"кавычках\"");
let x=1;
console.log(`Текст на нескольких
строках
со вставками выражений ${x} и ${(2*5)-3}`);
```
Преобразования из строки в число и обратно (попробуйте альтернативные варианты и неправильные значения):
```run-js
'use strict';
let x=15;
let y=x.toString(); // другой вариант String(x)
console.log(y, typeof y);
let s='10';
let n=Number(s); // другие варианты: +s parseInt(s) parseFloat(s)
console.log(n, typeof n);
```
Область видимости (scoping) переменной ограничена блоком в {} от места объявления. Глобальные переменные доступны везде после выполнения оператора объявления.
```run-js
'use strict';
let x=30;
{
let x=10;
console.log(x);
console.log(y);
let y=20;
}
console.log(x);
console.log(y);
```
Ранее переменные объявлялись с помощью [var](https://learn.javascript.ru/var), при этом областью видимости является вся функция, в которой объявлена переменная, включая код до объявления за счет поднятия (hoisting).
Кроме того, можно объявить переменную несколько раз (симптом ошибки программиста).
```run-js
{
var x=10;
console.log(x,y);
var y=20;
var x=30;
}
console.log(x,y);
```
Интерпретируется как
```js
var x,y;
{
x=10;
console.log(x,y);
y=20;
x=30;
}
console.log(x,y);
```
Арифметические [операции](https://learn.javascript.ru/operators)
такие же как в С. Перед выполнением операнды-строки преобразуются в числа, исключение - операция +. Для возведения в степень можно использовать ``**``.
```run-js
'use strict';
console.log(1/3);
console.log(2**0.5); // корень из 2
console.log('12'/'3');
console.log(1+'2');
console.log('1'+2);
```
Операции [сравнения](https://learn.javascript.ru/comparison) тоже аналогичные, но добавляется строгое равенство ``===``:
Основные операторы: if, while, for, switch - такие же как в Java или C.
Оператор ``if`` вычисляет выражение в скобках и преобразует результат к логическому типу.
Число 0, пустая строка ``""``, ``null``, ``undefined`` и ``NaN`` считаются ложными, остальные - истинными.
Переменная в ``switch`` проверяется на строгое равенство первому значению, затем второму и так далее.
```run-js
let browser='Firefox';
switch (browser) {
case 'Edge':
console.log('OK');
break;
case 'Chrome':
case 'Firefox':
case 'Safari':
console.log('Cool');
break;
default:
console.log('Change your browser!');
}
```
Операторы цикла:
```run-js
let n=3;
while(n>=0) {
console.log('n=',n);
--n;
}
let k=0;
do {
console.log('k=',k);
--k;
} while(k>0);
for (let i=0; i<10; i+=2) {
console.log('i=',i);
}
```
Есть операторы ``break`` (прервать выполнение цикла) и ``continue`` (перейти к следующей итерации).
*Задание 1:* напишите код, который выводит квадраты чисел от 1 до 10.
Для таких объявлений действует поднятие - можно обращаться к функции, определяемой ниже.
```run-js
'use strict';
function printsum() {
console.log(add(3,5));
}
function add(x,y) {
return x+y;
}
printsum();
```
Чаще всего в JavaScript используются [анонимные функции и стрелочные функции](https://learn.javascript.ru/function-expressions-arrows):
```run-js
'use strict';
let add1=function(x,y) { return x+y; }
let add2=(x,y) => x+y;
let factorial=function rec(x) { return x==0?1:x*rec(x-1); }
console.log(add1(3,5));
console.log(add2(30,50));
console.log(factorial(5));
console.log(typeof factorial);
```
Особенности: у стрелочных функций нет скрытого аргумента ``this``.
Для параметров можно указать значения по умолчанию:
См. также [остаточные параметры и ключевые параметры](https://learn.javascript.ru/destructuring-assignment)
[Замыкание](https://learn.javascript.ru/closure): Для имен в определении функции, не являющихся параметрами функции,
используются переменные из области видимости в момент определения функции со значениями в момент вызова.
```run-js
'use strict';
let x=1, y=2;
let func1;
{
let x=3;
func1=()=>x+y;
x=4;
}
console.log(func1());
let func2=()=>x+y;
console.log(func2());
```
*Задание 2*: напишите функцию `hypot(x,y)`, которая вычисляет длину гипотенузы по двум катетам.
*Задание 3*``*``: напишите функцию `"integr"(f,a,b,n)`, которая вычисляет интеграл функции `f` методом прямоугольников, разбивая отрезок `[a,b]` на `n` кусочков и вычисляя площадь прямоугольников под графиком функции.
Коллекции: массивы, объекты и классы
[Массив](https://learn.javascript.ru/array) - последовательность значений, нумеруется с 0:
```run-js
'use strict';
let arr=[10,1,20]; // массив создается перечислением элементов в []
console.log(typeof arr); // массив это объект класса Array
console.log(arr); // отладочная печать всего объекта
let arr2=[]; // пустой массив
// обычный способ, с возможностью изменения порядка
for(let i=0;i<arr.length;++i)
console.log(arr[i]);
// по всем элементам слева направо
for(let x of arr)
console.log(x);
arr.forEach((x)=>console.log(x)); // функциональный подход
```
Добавление и удаление значений в массив:
```run-js
'use strict';
let arr=[10,1,20];
delete arr[1]; // удаляем элемент
arr[6]=25;
console.log('существующие ключи');
for(let k in arr)
console.log(k,arr[k]);
console.log('все элементы');
for(let x of arr)
console.log(x);
console.log('уменьшаем размер до 2');
arr.length=2;
for(let x of arr)
console.log(x);
```
[Другие методы](https://learn.javascript.ru/array-methods) для работы с массивом, включают вставку и вырезание части массива, выделение подмассива, преобразования и сортировку.
*Задание 4*: Напишите функцию `"imax"(a)`, которая находит индекс минимального элемента в массиве `a`.
*Задание 5*: Напишите функцию `"conc"(a)`, которая сцепляет все строки в массиве `a` в одну строку. Пример: ``conc(['ab','cd','e'])`` должен вернуть строку ``'abcde'``.
[Объект](https://learn.javascript.ru/object) - ассоциативный массив, в котором ключом могут быть только строки или символы (для скрытых элементов).
```run-js
'use strict';
// создание объекта
let mykey='year salary';
let hidden=Symbol();
let person={ name: "John", age: 30, [mykey]: 100500, [hidden]: 'secret' };
console.log(typeof person); // это объект класса Object
console.log(person); // отладочная печать всего объекта
// обращение к элементу
console.log(person.name);
console.log(person['name']);
console.log(person[hidden]);
person.animal='dog'; // добавление элемента
delete person.animal; // удаление элемента
if(person.animal) // у персоны есть поле animal?
console.log(person.animal);
for(let key in person) // проход по ключам
console.log(`person.${key}=${person[key]}`);
```
*Задание 6*: Создайте объект ``engdict`` с англо-русским словарем. Напишите функцию `"translate"(a)`, которая заменяет английские слова в массиве `a` на их русский перевод, если они есть в словаре.
Есть ассоциативные коллекции [Map, Set](https://learn.javascript.ru/map-set),
[WeakMap, WeakSet](https://learn.javascript.ru/weakmap-weakset), в которых ключом могут значения любого типа.
[Классы](https://learn.javascript.ru/class) фактически реализуются на основе объектов.
[Наследование](https://learn.javascript.ru/class-inheritance) - только одиночное:
```run-js
'use strict';
class Person {
constructor(name,age) { // конструктор
this.age=age;
this.name = name;
}
sayHi() { console.log(`Hi, ${this.name}`); } // метод
}
class Manager extends Person {
sayHi() { console.log(`Hello, ${this.name}`); } // переопределение метода
}
let person1=new Person("John",30); // создание объекта
person1.sayHi(); // вызов метода
console.log(person1.age); // обращение к полю
let person2=new Manager("Charles",50); // создание объекта
person2.sayHi(); // вызов метода
```
Можно реализовать поля с проверкой при присваивании и вычисляемые поля.
```run-js
'use strict';
class Person {
constructor(name,age) {
this.age=age; // поле без контроля доступа
this.name = name; // поле с проверкой - вызываем setter
}
sayHi() { console.log(`Hi, ${this.name}`); }
get name() { return this._name; } // чтение поля с проверкой
set name(value) { // запись поля с проверкой
if (value.length > 10) {
console.log("Name too long.");
return;
}
this._name = value; // значение хранится в поле _name
}
get days() { return this.age*365; } // вычисляемое поле
}
let person = new Person("John",30);
person.name=person.name+" Frankenstein"; // используем getter и setter
console.log(person.days);
person.age++;
console.log(person.days);
person.animal='dog'; // можем добавлять новые элементы в объект
console.log(typeof person);
console.log(person instanceof Person); // проверка на принадлежность классу
// поля объекта их значения
for(let key in person)
console.log(`person.${key}=${person[key]}`);
// методы хранятся в специальном объекте-прототипе, общем для всех экземпляров класса
for(let name of Object.getOwnPropertyNames(Object.getPrototypeOf(person)))
console.log(`Person.prototype.${name}`);
```
*Задание 7*: Определите класс Склад, в который можно положить или забрать некоторое количество товара (наименование, количество единиц). Определите вычисляемое поле "суммарное количество единиц хранения".
Обработка ошибок
[Обработка ошибок](https://learn.javascript.ru/try-catch) есть и выполняется как в Java/C++.
```run-js
'use strict';
try {
let x;
x.run();
}
catch(err) {
console.log('Ошибка!!! '+err.message);
}
```