Тип данных Symbol
Символы нужны для создания уникальных идентификаторов. Использование:
- «Скрытые» свойства объектов.
- Системные символолы, доступные как
Symbol.*, например:-
Symbol.isConcatSpreadable -
Symbol.iterator -
Symbol.toPrimitive
-
Синтаксис:
let id = Symbol("id"); // Символ с описанием (именем) "id"
let user = {
[id]: 12,
};
user[id] = 123;
Символы уникальны (не равны друг другу), даже если имеют одинаковое описание.
Для любых символов доступно свойство description:
id.description; // id
Символы игнорируются циклом for…in
Символы не преобразуются автоматически в строки:
alert(id); // TypeError: Cannot convert a Symbol value to a string
Методы для получения символов:
Object.getOwnPropertySymbols(obj)возвращает массив символьных ключей объектаReflect.ownKeys(obj)возвращает массив всех собственных ключей объекта, включая символьные
Объекты
"key" in object // оператор "in" определяет существование свойста
Глубокое клонирование объектов можно осуществить с помощью рекурсии, воспользовавшись глобальным методом structuredClone(obj), либо использовать метод _.cloneDeep(obj) из JavaScript-библиотеки lodash.
Получение свойств объекта
Object.getOwnPropertyNames(obj) возвращает не-символьные ключи.
Object.getOwnPropertySymbols(obj) возвращает символьные ключи.
Object.keys/values() возвращают не-символьные ключи/значения с флагом enumerable.
for..in перебирает не-символьные ключи с флагом enumerable, а также ключи прототипов.
Reflect.ownKeys(obj) возвращает все собственные ключи объекта, включая символьные.
Сборка мусора
Основной алгоритм сборки мусора – «алгоритм пометок» («mark-and-sweep»).
- Сборщик мусора «помечает» (запоминает) все корневые объекты.
- Затем он идёт по их ссылкам и помечает все найденные объекты.
- Затем он идёт по отмеченным объектам и отмечает их ссылки. Все посещённые объекты запоминаются, чтобы в будущем не посещать один и тот же объект дважды.
- …И так далее, пока не будут посещены все достижимые (из корней) ссылки.
- Все непомеченные объекты удаляются.
"this"
В JavaScript this является «свободным», его значение вычисляется в момент вызова метода и не зависит от того, где этот метод был объявлен, а зависит от того, какой объект вызывает метод (какой объект стоит «перед точкой»). Вызов функции без объекта означает, что this === undefined.
Значение this передаётся правильно, только если функция вызывается напрямую с использованием синтаксиса точки obj.method(). При любой другой операции, например, присваивании obj = obj.method, this теряется и дальнейший вызов происходит уже без this.
let user = {
age: 20,
f1() { return this.age; },
f2() { return 1; }
};
(user.age == 20 ? user.f1 : user.f2)(); // TypeError: Cannot read properties of undefined
Конструкторы, создание объектов через "new"
Конструкторы являются обычными функциями. Но есть два соглашения:
- Имя такой функции должно начинаться с большой буквы.
- Функция должна вызываться только с помощью оператора
new.
const user = new User(...)
При вызове функции-конструктора при помощи оператора new происходит следующее:
- Создаётся новый пустой объект, и он присваивается
this//this = {};(неявно). - Выполняется код функции. Обычно он модифицирует
this, добавляет туда новые свойства. - Возвращается значение
this//return this;(неявно).
Если функция-конструктор возвращает примитивное значение, то оно будет отброшено.
Если же возвращаемое значение - объект, то вместо this будет возвращён этот объект.
Проверка на вызов в режиме конструктора: new.target
В случае обычного вызова функции new.target === undefined. Если же она была вызвана при помощи new, new.target будет равен самой функции.
function User(name) {
if (!new.target) { // в случае, если вы вызвали меня без оператора new
return new User(name); // ...я добавлю new за вас
}
this.name = name;
}
Опциональная цепочка
Опциональная цепочка ?. останавливает вычисление и возвращает undefined, если часть перед ?. имеет значение undefined или null.
{}.address.street // TypeError: Cannot read properties of undefined (reading 'street')
{}.address?.street // undefined (без ошибки)
Переменная перед ?. должна быть объявлена, иначе выражение выдаст ошибку.
Другие варианты применения: ?.(), ?.[].
Преобразование объектов в примитивы
Существует "три хинта", то есть 3 варианта преобразования объекта в примитив: "string", "number", "default".
В процессе преобразования движок JavaScript пытается найти и вызвать три следующих метода объекта:
obj[Symbol.toPrimitive](hint)obj.toString()obj.valueOf()
Порядок вызова:
- Вызывает
obj[Symbol.toPrimitive](hint)если такой метод существует, и передаёт ему хинт. - Иначе, если хинт равен
"string"
пытается вызватьobj.toString(), а если его нет, тоobj.valueOf(), если он существует. - В случае, если хинт равен
"number"или"default"
пытается вызватьobj.valueOf(), а если его нет, тоobj.toString(), если он существует.
Symbol.toPrimitive - это универсальный подход:
let user = {
name: "John",
money: 1000,
[Symbol.toPrimitive](hint) {
return hint == "string" ? `{name: "${this.name}"}` : this.money;
}
};
alert(user); // {name: "John"} -> hint: string
alert(+user); // 1000 -> hint: number
alert(user + 500); // 1500 -> hint: default
На практике довольно часто достаточно реализовать только obj.toString().
Он обработает все случаи преобразований к примитивам.
Числа
Number.isInteger(num) возвращает true, если num - целое число, иначе false.
num.toFixed(n) округляет число до n знаков после запятой и возвращает строковое представление результата.
let num = 1.396;
num.toFixed(2) === '1.40';
Шестнадцатеричные, двоичные и восьмеричные числа:
0x - 16-ые, 0b - 2-ые, 0o - 8-ые.
0xff === 255;
0b1111 === 15;
Метод num.toString(base) возвращает строковое представление числа в системе счисления base, где base от 2 до 36.
20..toString(2) === '10100'; // 2 точки для вызова метода
(20).toString(2) === '10100'; // либо круглые скобки
Неточные вычисления
0.1 + 0.2 == 0.3; // false
Число хранится в памяти в бинарной форме.
Дроби, такие как 0.1 и 0.2 являются бесконечной дробью в двоичной форме.
В JavaScript нет возможности для хранения точных значений 0.1 или 0.2, используя двоичную систему, так же, как нет возможности хранить одну третью в десятичной системе счисления.
Из 64 бит, отведённых на число, сами цифры числа занимают до 52 бит, остальные 11 бит хранят позицию десятичной точки и один бит – знак. Eсли 52 бит не хватает на цифры, то при записи пропадут младшие разряды.
9999999999999999 === 1e16; // true
Проверка: isFinite и isNaN
Значение NaN уникально тем, что оно не равно ничему, даже самому себе.
NaN == NaN; // false
isNaN(value) преобразует значение в число и проверяет является ли оно NaN.
isFinite(value) преобразует значение в число и возвращает true, если оно не NaN/Infinity/-Infinity.
Number.isNaN(value) возвращает true в случае, если значение принадлежит к типу number и является NaN (без преобразования типа).
Number.isFinite(value) возвращает true в случае, если значение принадлежит к типу number и не является NaN/Infinity/-Infinity (без преобразования типа).
Object.is(a, b) проверяет на строгое равенство, может работать с NaN и нулем.
Object.is(NaN, NaN) === true;
Object.is(-0, 0) === false; // технически эти значения разные
parseInt и parseFloat
parseInt(str, base) и parseFloat(str) «читают» число из строки. Если в процессе чтения возникает ошибка, они возвращают полученное до ошибки число. Eсли первый символ - не число, возвращают NaN.
parseInt возвращает целое число, второй параметр определяет систему счисления (от 2 до 36).
parseFloat возвращает число с плавающей точкой.
parseInt('100px') === 100
parseFloat('12.5em') === 12.5
parseFloat('12.3.4') === 12.3
parseInt('2n9c', 36) === 123456
Методы Math.*
Math.floor(num) - округляет в меньшую сторону
Math.ceil(num) - округляет в большую сторону
Math.round(num) - округляет до ближайшего целого
Math.trunc(num) - удаляет дробную часть без округления
Math.abs(num) - возвращает число по модулю
Math.random() - возвращает псевдослучайное число n, 0 <= n < 1
Math.max(...nums) - возвращает наибольшее число
Math.min(...nums) - возвращает наименьшее число
Math.pow(n, power) - возвращает число n, возведённое в степень power
Строки
Спецсимволы
\n- перевод строки\t- знак табуляции\',\"- кавычки\\- обратный слеш (\)
JavaScript позволяет вставить символ в строку, указав его шестнадцатеричный Юникод с помощью одной из трех нотаций:
\xXX– можно использовать только для первых 256 символов Юникода\uXXXX– кодировка одного из первых 65536 символов Юникода\u{X…XXXXX}– любой символ от 0 до 10FFFF
"\xA9" === "©";
"\u{1F680}" === "🚀";
Основные методы
str.charAt(pos) - возвращает символ на позиции pos, при отсутствии возвращают пустую строку.
str.at(pos) - возвращает символ на позиции pos, при отсутствии возвращает undefined. Если задать отрицательное число, то отсчет ведется от конца строки.
"Hello".at(-1) === "o";
str.toLowerCase() и str.toUpperCase() возвращают строку в заданном регистре.
str.indexOf(substr, pos) ищет подстроку substr в строке str, начиная с позиции pos, и возвращает позицию совпадения, либо -1 при отсутствии совпадений.
str.lastIndexOf(substr, pos) ищет с конца строки (или с pos) до начала.
"some string".indexOf("s", 1) === 5;
"some string".lastIndexOf("s") === 5;
str.includes(substr, pos) возвращает true, если в строке str есть подстрока substr, либо false, если нет.
"Midget".includes("id", 2) === false; // поиск начат с позиции 2
str.startsWith(substr) и str.endsWith(substr) проверяют, начинается ли и заканчивается ли строка определённой строкой.
"Widget".startsWith("Wid") === true;
str.slice(start, end) возвращает часть строки от start до (не включая) end.
"stringify".slice(0, 5) === "strin";
"stringify".slice(2) === "ringify";
"stringify".slice(-4, -1) === "gif";
str.substring(start, end) - почти такой же. Можно задавать start больше end, отрицательные значения интерпретируются как 0.
"stringify".substring(6, 2) === "ring";
"stringify".substring(2, -3) === "st";
str.split(regexp|delim, limit) - разбивает строку на массив по заданному разделителю delim. Если delim не указан, возвращает всю строку в первом элементе массива.
Имеет необязательный второй числовой аргумент – ограничение на количество элементов в массиве. Если их больше, чем указано, то остаток массива будет отброшен.
"12345".split('', 2); // ['1', '2']
str.trim() — убирает пробелы в начале и конце строки
str.repeat(n) — повторяет строку n раз
Стравнение строк
Строки кодируются в UTF-16. У любого символа есть соответствующий код.
str.codePointAt(pos) возвращает код для символа, находящегося на позиции pos.
"z".codePointAt(0) === 122;
"a".codePointAt() === 97; // pos === 0 при отсутсвии аргумента
"Z".codePointAt(0) === 90;
String.fromCodePoint(code) создаёт символ по его коду code.
String.fromCodePoint(90) === "Z"
str.codePointAt(pos) и String.fromCodePoint(code) правильно обрабатывают суррогатные пары.
'𝒳'.codePointAt(0) === 119987;
"\u{1d4b3}" === '𝒳'; // (119987).toString(16) === "1d4b3"
String.fromCodePoint(119987) === '𝒳';
str.localeCompare(str2) показывает, какая строка больше в соответствии с правилами языка.
Возвращает 1 если str больше str2, -1 если str меньше, 0 если строки равны.
Массивы
let arr = new Array(); // эквивалентно let arr = [];
new Array("Яблоко", "Груша", "и тд"); // можно сразу добавить элементы
Однако, если new Array вызывается с одним аргументом, который представляет собой число, он создаёт массив без элементов, но с заданной длиной.
Методы
arr.at(pos) - возвращает элемент на позиции pos, pos может быть отрицательным.
[1, 2, 3].at(-1) === 3
arr.pop() - удаляет последний элемент из массива и возвращает его.
arr.push(elem1, elem2...) - добавляет элемент (или несколько элементов) в конец массива и возвращает длину получившегося массива.
arr.shift() - удаляет из массива первый элемент и возвращает его.
arr.unshift(elem1, elem2...) - добавляет элемент (или несколько элементов) в начало массива и возвращает длину получившегося массива.
arr.splice(index[, deleteCount, elem1, ..., elemN]) - удаляет deleteCount элементов начиная с index и заменяет на elemN эл-ты. Возвращает массив удаленных элементов. Может вставлять элементы без удаления если установить deleteCount в 0.
arr.slice(start, end) - возвращает новый массив, в который копирует элементы, начиная с индекса start и до (не включая) end. Без аргументов создаёт копию массива arr.
arr.concat(arg1, arg2...) - создаёт новый массив, в который распаковывает данные из других массивов и добавляет дополнительные значения. Другие объекты добавляются как есть.
Если псевдомассив имеет специальное свойство Symbol.isConcatSpreadable со значением truthy, он обрабатывается как массив.
[1, 2].concat([[3, 4], 5], 6, {7: 8}) // вернет [1, 2, [3, 4], 5, 6, {7: 8}]
arr.forEach(function(item, index, array) {...}) - позволяет запускать функцию для каждого элемента массива. Результат функции, если она что-то возвращает, игнорируется.
arr.indexOf(item, from) - ищет item в массиве, начиная с позиции from и возвращает индекс найденного элемента, либо -1 если ничего не найдено.
arr.lastIndexOf(item, from) - поиск выполняется начиная с from и заканчивая началом массива.
arr.includes(item, from) - ищет item начиная с индекса from и возвращает true, если поиск успешен, false в противном случае. Метод правильно обрабатывает NaN.
[NaN].includes(NaN) === true
arr.find(function(item, index, array) {...}) - возвращает item, при котором функция вернула truthy, иначе undefined.
arr.findIndex(function(item, index, array) {...}) - возвращает индекс, при котором функция вернула truthy, иначе -1.
arr.filter(function(item, index, array) {...}) - возвращает массив из всех подходящих элементов (для которых функция вернула truthy). Возвращается пустой массив в случае, если таких элементов нет.
arr.map(function(item, index, array) {...}) - вызывает функцию для каждого элемента массива и возвращает массив результатов выполнения этой функции.
["Bilbo", "Gandalf", "Nazgul"].map(item => item.length) // [5, 7, 6]
arr.sort(fn) - возвращает отсортированный массив (возвращенный массив и есть arr).
По умолчанию эл-ты сортируются как строки. Чтобы использовать собственный порядок сортировки, нужно предоставить функцию fn в качестве аргумента.
Эта функция принимает два параметра, назовем их a и b (a - элемент справа). Если функция сравнения этих двух элементов возвращает отрицательное число, элементы меняются местами.
[2, 3, 1].sort( (a, b) => a - b ); // [1, 2, 3]
['Österreich', 'Andorra', 'Vietnam'].sort( (a, b) => a.localeCompare(b) ); // правильная сортировка строк
arr.reverse() - меняет порядок элементов в arr на обратный (возвращенный массив и есть arr).
arr.join(glue) - возвращает строку из элементов arr, вставляя glue между ними (запятую если glue не указан).
arr.reduce(function(previousValue, item, index, array) {...}, [initial]) - используется для вычисления единого значения на основе всего массива.
- метод перебирает массив
- при вызове функции результат её вызова передается в
previousValue - первоначально
previousValueберется изinitial - метод возвращает результат конечной функции
Если массив пустой, то возвращается initial, иначе, если не указан initial, вызов завершается ошибкой TypeError: Reduce of empty array with no initial value.
arr.reduceRight(function(previousValue, item, index, array) {...}, [initial]) - работает аналогично, проходя по массиву справа налево.
Array.isArray - используется чтобы отличить простой объект от массива
Array.isArray([]) === true
«thisArg»
arr[method](func, thisArg)
Почти все методы массива, которые вызывают функции, за исключением sort и reduce, принимают необязательный параметр thisArg. Значение параметра thisArg становится this для func.
let army = {
minAge: 18,
maxAge: 27,
canJoin(user) {
return user.age >= this.minAge && user.age < this.maxAge;
}
};
let users = [{age: 16}, {age: 20}, {age: 23}];
users.filter(army.canJoin, army); // [{age: 20}, {age: 23}]
Другие методы
arr.some(fn) и arr.every(fn) проверяют массив. Функция fn вызывается для каждого элемента массива. Если какие-либо/все результаты вызовов являются truthy, то метод возвращает true, иначе false.
arr.fill(value, start, end) – заполняет массив повторяющимися value, начиная с индекса start до end. Меняет только уже существующие элементы (возвращенный массив и есть arr).
arr.copyWithin(target, start, end) – копирует свои элементы, начиная со start и заканчивая end, на позиции, начиная с target (возвращенный массив и есть arr).
arr.flat(depth) - создаёт новый массив из всех подмассивов в нём. Он принимает один параметр — глубину «сглаживания» массива.
[1, [2, [3, [4]], 5]].flat(1); // [1, 2, [3, [4]], 5];
[1, [2, [3, [4]], 5]].flat(Infinity); // [1, 2, 3, 4, 5];
arr.flatMap(fn) - сначала он вызывает mapping-функцию fn для каждого элемента в массиве, а потом «выравнивает» их в один массив.
['Это предложение', 'Это другое предложение'].flatMap(sentence => sentence.split(' ')) // ['Это', 'предложение', 'Это', 'другое', 'предложение']
Перебираемые объекты
Итерируемые объекты – объекты, которые реализуют метод Symbol.iterator.
Псевдомассивы – объекты, у которых есть индексы и свойство length.
Symbol.iterator
Чтобы сделать объект итерируемым (и позволить for..of работать с ним), нужно добавить в объект метод с именем Symbol.iterator.
- Когда цикл
for..ofзапускается, он вызывает этот метод один раз (или выдаёт ошибку, если метод не найден). Этот метод должен вернуть итератор – объект с методомnext. - Дальше
for..ofработает только с этим возвращённым объектом. - Когда
for..ofхочет получить следующее значение, он вызывает методnext()этого объекта. - Результат вызова
next()должен иметь вид{done: Boolean, value: any}, гдеdone: trueозначает, что итерация закончена, в противном случаеvalueсодержит очередное значение.
let range = {
from: 1,
to: 5
};
range[Symbol.iterator] = function() {
return {
current: this.from,
last: this.to,
next() {
if (this.current <= this.last) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
};
for (let num of range) {
console.log(num); // 1, затем 2, 3, 4, 5
}
Array.from
Array.from(obj[, mapFn, thisArg]) принимает итерируемый объект или псевдомассив и делает из него «настоящий» Array. Позволяет указать необязательную «трансформирующую» функцию.
Array.from(range, num => num * num); // [1, 4, 9, 16, 25] (range взят из примера выше)
Object.keys, values, entries
Object.keys(obj) – возвращает массив ключей.
Object.values(obj) – возвращает массив значений.
Object.entries(obj) – возвращает массив пар [ключ, значение].
Object.fromEntries(arr) - обратный метод. Преобразует массив пар [ключ, значение] в объект.
Object.fromEntries( Object.entries( {a: 1, b: 2} ).map(([key, value]) => [key, value * 2]) ); // {a: 2, b: 4}
Map, Set, WeakMap, WeakSet
Map
Map – это коллекция пар ключ/значение, как и Object. Позволяет использовать ключи любого типа.
При создании Map можно указать массив (или другой итерируемый объект) с парами ключ-значение.
new Map([
['1', 'str1'],
[1, 'num1'],
[true, 'bool1']
]); // создает коллекцию из трех пар [ключ, значение]
map.set(key, value) – записывает по ключу key значение value. Возвращает получившийся map.
map.get(key) – возвращает значение по ключу или undefined, если ключ key отсутствует.
map.has(key) – возвращает true, если ключ key присутствует в коллекции, иначе false.
map.delete(key) – удаляет элемент по ключу key. Возвращает true, если элемент был удален, иначе false.
map.clear() – очищает коллекцию от всех элементов.
map.size – возвращает текущее количество элементов.
map.set("1", "str1").set(1, "num1").set(true, "bool1"); // цепочка вызовов
Перебор Map
map.keys() – возвращает итерируемый объект по ключам.
map.values() – возвращает итерируемый объект по значениям.
map.entries() – возвращает итерируемый объект по парам вида [ключ, значение], используется по умолчанию в for..of.
map.forEach((value, key, map) => {...} ) - схож с одноименным методом для массивов
for (let key of map.keys()) {
console.log(key); // выводит все ключи map
}
for (let [key, value] of map) { ... } // получить и ключи, и значения
Object.fromEntries: Object из Map
Object.fromEntries(map.entries()); // возвращает объект
Object.fromEntries(map); // так тоже работает
Set
Объект Set – особый вид коллекции: «множество» уникальных значений без ключей, где каждое значение может появляться только один раз.
new Set(iterable) – создаёт Set, и если в качестве аргумента был предоставлен итерируемый объект (обычно это массив), то копирует его значения в новый Set.
set.add(value) – добавляет значение (если оно уже есть, то ничего не делает), возвращает получившийся set.
set.delete(value) – удаляет значение, возвращает true, если value было в множестве на момент вызова, иначе false.
set.has(value) – возвращает true, если значение присутствует в множестве, иначе false.
set.clear() – удаляет все имеющиеся значения.
set.size – возвращает количество элементов в множестве.
Перебор Set
set.keys(), set.values() – возвращают перебираемый объект для значений. Оба метода работают одинаково.
set.entries() – возвращает перебираемый объект с парами [значение, значение]
for (let value of set) {...} // используя for..of
set.forEach((value, valueAgain, set) => { ... } ); // или forEach
WeakMap
WeakMap и WeakSet используются как вспомогательные структуры данных в дополнение к «основному» месту хранения объекта. Не являются перебираемыми.
Ключи в WeakMap должны быть объектами.
При использовании объекта в качестве ключа если больше нет ссылок на этот объект, он будет удалён из памяти (и из объекта WeakMap) автоматически вместе с соответствующим ему значением.
weakMap.get(key)
weakMap.set(key, value)
weakMap.delete(key)
weakMap.has(key)
let messages = [
{text: "Hello", from: "John"},
{text: "How goes?", from: "John"},
{text: "See you soon", from: "Alice"}
];
let readMap = new WeakMap();
readMap.set(messages[0], new Date(2017, 1, 1));
WeakSet
В WeakSet можно добавлять только объекты.
Объект присутствует в множестве только до тех пор, пока доступен где-то ещё.
weakSet.add(value)
weakSet.has(value)
weakSet.delete(value)
Деструктурирующее присваивание
Это специальный синтаксис, который позволяет «распаковать» перебираемые объекты в несколько переменных.
Деструктуризация массива
let [first, second] = [1, 2]; // first === 1, second === 2
Ненужные элементы массива могут быть отброшены через запятую.
let [first, , third] = [1, 2, 3, 4];
Можно использовать что угодно «присваивающее» с левой стороны.
let user = {}; // преобразуется в {name: 'John', surname: 'Smith'}
[user.name, user.surname] = "John Smith".split(' ');
Остаточные параметры.
let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "Me"];
console.log(rest); // ['Consul', 'Me']
Если в массиве меньше значений, чем в присваивании, они считаются неопределёнными.
let [firstName, surname] = []; // firstName === undefined
Значения по умолчанию.
let [name = "Guest", info = prompt('')] = []; // info - результат prompt
Деструктуризация объекта
Деструктурирующее присваивание также работает с объектами.
let {height = 100, width} = { width: 200 }; // height === 100, width === 200
Можно присвоить свойство объекта переменной с другим названием.
let { title: t } = { title: "Menu" }; // t === "Menu"
Для получения остатка объекта можно использовать троеточие, как и для массивов.
let {one, ...rest} = { one: 1, two: 2, three: 3 };
console.log(rest); // {two: 2, three: 3}
Вложенная деструктуризация
let options = { size: {width: 100, height: 200} };
let {
size: {
width: w,
height,
title = "Menu",
},
} = options;
console.log(height); // 200
console.log(w); // 100
console.log(title); // "Menu"
console.log(size); // ReferenceError: size is not defined
console.log(width); // ReferenceError: width is not defined
Умные параметры функций
Есть ситуации, когда функция имеет много параметров, большинство из которых не обязательны.
function showMenu(title = "Untitled", width = 200, height = 100, items = []) {...}
Можно передать параметры как объект, и функция немедленно деструктурирует его в переменные.
function showMenu({title = "Untitled", width = 200, height = 100, items = []}) {...}
showMenu({ title: "My menu", items: ["Item1", "Item2"] });
Если нужны значения по умолчанию, то следует передать пустой объект.
showMenu({}); // все значения - по умолчанию
showMenu(); // TypeError: Cannot read properties of undefined (reading 'title')
Ошибку можно исправить, сделав {} значением по умолчанию для всего объекта параметров.
function showMenu({ title = "Menu", width = 100, height = 200 } = {}) {
console.log( `${title} ${width} ${height}` );
}
showMenu(); // Menu 100 200
Дата и время
Создание объекта Date
new Date() – создать с текущими датой и временем.
new Date(milliseconds) - таймстамп (кол-во миллисекунд, прошедших с 1 января 1970 года).
new Date(datestring) - строка в формате YYYY-MM-DDTHH:mm:ss.sssZ (обязателен только год).
Tиспользуется в качестве разделителяZобозначает часовой пояс в формате+-hh:mm. Если указать просто буквуZ, получим UTC+0
new Date('2022-01-26T13:51:50.417-07:00'); // Wed Jan 26 2022 23:51:50 GMT+0300 (Москва, стандартное время)
new Date(year, month, date, hours, minutes, seconds, ms) - аргументы (обязательны первые два).
yearдолжен состоять из четырёх цифрmonthнумеруется от0(январь) по11(декабрь)
new Date(2021, 5, 6, 8, 49); // Sun Jun 06 2021 08:49:00 GMT+0300
Автоисправление даты
Используя последний вариант можно устанавливать компоненты даты вне обычного диапазона значений (даже нулевые или отрицательные), объект сам себя исправит.
new Date(2013, 0, 32); // Fri Feb 01 2013 ...
Получение компонентов даты
Методы возвращают значения в соответствии с местным часовым поясом:
getFullYear() - возвращает год (4 цифры).
getMonth() - возвращает месяц, от 0 до 11.
getDate() - возвращает день месяца, от 1 до 31.
getHours(), getMinutes(), getSeconds(), getMilliseconds() - соответственно названиям.
getDay() - возвращает день недели от 0 (воскресенье) до 6 (суббота).
Их UTC-аналоги:
getUTCFullYear(), getUTCMonth(), getUTCDate(), getUTCHours(), getUTCMinutes(), getUTCSeconds(), getUTCMilliseconds(), getUTCDay()
new Date().getHours(); // 9
new Date().getUTCHours(); // 6
2 особых методы без UTC-варианта:
getTime() - для заданной даты возвращает таймстамп
new Date().getTime(); // 1688275690962
getTimezoneOffset() - возвращает разницу в минутах между местным часовым поясом и UTC.
new Date().getTimezoneOffset(); // -180
Установка компонентов даты
Методы изменяют дату в соответствии с местным часовым поясом и возвращают таймстамп.
setFullYear(year, [month], [date])
setMonth(month, [date])
setDate(date)
setHours(hour, [min], [sec], [ms])
setMinutes(min, [sec], [ms])
setSeconds(sec, [ms])
setMilliseconds(ms)
let date = new Date(2021, 5, 6, 8, 49);
date.setHours(0);
console.log( date ); // Sun Jun 06 2021 00:49:00 GMT+0300
setTime(milliseconds) - устанавливает дату в виде целого кол-во ms, прошедших с 01.01.1970 UTC
Если нужно изменить дату:
let date = new Date(2016, 1, 28);
date.setDate(date.getDate() + 2);
console.log(date); // Tue Mar 01 2016 ...
Преобразование к числу, разность дат.
Если объект Date преобразовать в число, то получим таймстамп по аналогии с date.getTime().
+new Date(); // 1688280813375
Поэтому даты можно вычитать, получая разность в миллисекундах.
Date.now() - метод для быстрого получения текущего времени в формате таймстампа. Семантически эквивалентен new Date().getTime(), но не создаёт промежуточный объект Date. Так что этот способ работает быстрее и не нагружает сборщик мусора.
Разбор строки с датой
Date.parse(str) считывает дату из строки.
Формат такой же, как в случае с new Date(datestring).
Вызов Date.parse(str) обрабатывает строку в заданном формате и возвращает таймстамп. Если формат неправильный, возвращается NaN.
performance.now()
В браузерах есть метод performance.now(), возвращающий количество миллисекунд с начала загрузки страницы с точностью до микросекунд (корректными являются только три цифры после точки):
performance.now() // 8232.939999899827 (8.232939 секунды)
Формат JSON, метод toJSON
Формат JSON (JavaScript Object Notation):
Ключи объектов заключаются в двойные кавычки. Значения, если это строки, также заключаются в двойные кавычки.
JSON поддерживает простые объекты, массивы, строки, числа, логические значения и null.
JSON.stringify
JSON.stringify(value[, replacer, space]) - метод для преобразования объектов в JSON.
value - объект для преобразования в JSON.
replacer - массив свойств для кодирования или функция соответствия function(key, value).
space - указывает на кол-во пробелов для удобного форматирования.
JSON.stringify(user, null, 2);
JSON является независимой от языка спецификацией для данных, поэтому JSON.stringify пропускает некоторые специфические свойства объектов JavaScript:
- cвойства-функции (методы)
- cимвольные ключи и значения
- свойства со значением
undefined
Ограничение: не должно быть циклических ссылок.
let meetup = {
title: "Conference",
participants: [{name: "John"}, {name: "Alice"}],
}
Вместо replacer можно передать массив свойств, которые мы хотим записать в JSON:
JSON.stringify(meetup, ['title', 'participants']); // '{"title":"Conference","participants":[{},{}]}'
Также на месте replacer можно написать функцию, а не массив. Она будет вызываться для каждой пары (key, value) и должна возвращать замененное значение вместо исходного, либо undefined чтобы пропустить значение.
JSON.stringify(meetup, function replacer(key, value) {
return (value == 'John') ? undefined : value;
}); // '{"title":"Conference","participants":[{},{"name":"Alice"}]}'
Первый вызов – особенный. Ему передаётся специальный «объект-обёртка»: {"": meetup}. Другими словами, первая (key, value) пара имеет пустой ключ, а значением является целевой объект в общем.
JSON.stringify(meetup, function replacer(key, value) {
if (value === meetup) console.log(key === '')
}); // true
Пользовательский «toJSON»
Если объект имеет метод toJSON для преобразования в JSON, то он вызывается через JSON.stringify:
let room = {
number: 23,
toJSON() {
return this.number;
}
};
let meetup = {
title: "Conference",
room,
};
JSON.stringify(meetup); // {"title":"Conference","room":23}
JSON.parse
JSON.parse(str, [reviver]) - метод для преобразования JSON обратно в объект.
str - это сам JSON для преобразования в объект.
reviver - необязательная ф-ция, которая будет вызываться для каждой пары (ключ, значение) и преобразовывать значения.
Например, используется чтобы значения объектов date не оставались строками после преобразования:
let obj = {date: new Date()};
let json = JSON.stringify(obj); // "2023-07-02T09:37:28.557Z"
JSON.parse(json, function(key, value) {
if (key === 'date') return new Date(value);
return value;
}); // {date: Sun Jul 02 2023 12:40:39 ... }