update objects

This commit is contained in:
2025-02-28 10:31:23 +03:00
parent b021fab251
commit 7d94ca6021
19 changed files with 346 additions and 216 deletions

View File

@@ -87,7 +87,7 @@ alert(obj.__proto__); // [object Object], значение - это объект
В отличие от многих других языков, особенность JavaScript-объектов в том, что можно получить доступ к любому свойству. Даже если свойства не существует ошибки не будет!
При обращении к свойству, которого нет, возвращается undefined. Это позволяет просто проверить существование свойства:
При обращении к свойству, которого нет, возвращается **undefined**. Это позволяет просто проверить существование свойства:
```js
let user = {};

View File

@@ -0,0 +1,76 @@
---
sidebar_position: 2
---
# Копирование объектов и ссылки
💥 ***При копировании переменной объекта копируется ссылка, но сам объект не дублируется.***
```js
let user = { name: 'John' };
let admin = user;
admin.name = 'Pete'; // изменено по ссылке из переменной "admin"
alert(user.name); // 'Pete', изменения видны по ссылке из переменной "user"
```
## Сравнение по ссылке
Два объекта равны только в том случае, если это один и тот же объект.
***a*** и ***b*** ссылаются на один и тот же объект, поэтому они равны:
```js
let a = {};
let b = a; // копирование по ссылке
alert( a == b ); // true, обе переменные ссылаются на один и тот же объект
alert( a === b ); // true
```
Здесь два независимых объекта не равны, даже если они выглядят одинаково (оба пусты):
```js
let a = {};
let b = {}; // два независимых объекта
alert( a == b ); // false
```
## Клонирование и объединение, Object.assign
Есть 2 вида копирования shallow - поверхностное и deep - глубокое
#### Перебор всех свойств объекта через цикл или рекурсию (shallow / deep)
```js
let user = {
name: "John",
age: 30
};
let clone = {}; // новый пустой объект
// давайте скопируем все свойства user в него
for (let key in user) {
clone[key] = user[key];
}
// теперь clone это полностью независимый объект с тем же содержимым
clone.name = "Pete"; // изменим в нём данные
alert( user.name ); // все ещё John в первоначальном объекте
```
Готовая реализация, через например _.cloneDeep(obj) из библиотеки JavaScript lodash.
#### Метод Object.assign (shallow)
```js
Object.assign(dest, [src1, src2, src3...])
```
#### Оператор расширения \{...obj\} (shallow)
Метод тоже делает поверхностное копирование, для глубокого надо обойти объект рекурсивно
#### JsonParse (deep)
Cамый простой способ скопировать объект со всеми уровнями вложенности, не копирует методы:
```js
let clone = JSON.parse(JSON.stringify(obj))
```
#### Метод structuredClone (deep)
Мы можем использовать глобальный метод ***structuredClone()***, который позволяет сделать полную копию объекта. К сожалению он поддерживается только современными браузерами.

View File

@@ -0,0 +1,40 @@
---
sidebar_position: 3
---
# Методы объекта, "this"
***Для доступа к информации внутри объекта метод может использовать ключевое слово this.***
```js
let user = {
name: "John",
sayHi() {
alert(this.name); // "this" - это "текущий объект".
}
};
```
### «this» не является фиксированным
В JavaScript ключевое слово «this» ведёт себя иначе, чем в большинстве других языков программирования. Его можно использовать в любой функции, даже если это не метод объекта.
***Вызов без объекта: this == undefined***
В строгом режиме ("use strict") в таком коде значением `this` будет являться `undefined`. Если мы попытаемся получить доступ к this.name это вызовет ошибку.
В нестрогом режиме значением `this` в таком случае будет глобальный объект (`window` в браузерe, мы вернёмся к этому позже в главе Глобальный объект). Это исторически сложившееся поведение `this`, которое исправляется использованием строгого режима ("use strict").
Обычно подобный вызов является ошибкой программирования. Если внутри функции используется this, тогда она ожидает, что будет вызвана в контексте какого-либо объекта.
***Последствия свободного this***
В JavaScript `this` является «свободным», его значение вычисляется в момент вызова метода и не зависит от того, где этот метод был объявлен, а скорее от того, какой объект вызывает метод (какой объект стоит «перед точкой»).
Эта концепция вычисления `this` в момент исполнения имеет как свои плюсы, так и минусы. С одной стороны, функция может быть повторно использована в качестве метода у различных объектов (что повышает гибкость).
### У стрелочных функций нет «this»
Стрелочные функции особенные: у них нет своего «собственного» `this`. Если мы ссылаемся на `this` внутри такой функции, то оно берётся из внешней «нормальной» функции.
1. Методы могут ссылаться на объект через `this`.
2. Значение `this` определяется во время исполнения кода.
- При объявлении любой функции в ней можно использовать `this`, но этот `this` не имеет значения до тех пор, пока функция не будет вызвана.
- Когда функция вызывается синтаксисом «метода» ***object.method()***, значением `this` во время вызова является `object`.

View File

@@ -0,0 +1,34 @@
---
sidebar_position: 4
---
# Конструктор, оператор "new"
### Функция-конструктор
Функции-конструкторы технически являются обычными функциями. Но есть два соглашения:
1. Имя функции-конструктора должно начинаться с большой буквы.
2. Функция-конструктор должна выполняться только с помощью оператора "new".
Когда функция вызывается как new User(...), происходит следующее:
1. Создаётся новый пустой объект, и он присваивается `this`.
2. Выполняется тело функции. Обычно оно модифицирует `this`, добавляя туда новые свойства.
3. Возвращается значение `this`.
Другими словами, new User(...) делает что-то вроде:
```js
function User(name) {
// this = {}; (неявно)
// добавляет свойства к this
this.name = name;
this.isAdmin = false;
// return this; (неявно)
}
```
Итого:
- Функции-конструкторы или просто конструкторы, являются обычными функциями, но существует общепринятое соглашение именовать их с заглавной буквы.
- Функции-конструкторы следует вызывать только с помощью `new`. Такой вызов подразумевает создание пустого `this` в начале и возврат заполненного в конце.

View File

@@ -0,0 +1,36 @@
---
sidebar_position: 5
---
# Опциональная цепочка '?.'
Опциональная цепочка ?. останавливает вычисление и возвращает `undefined`, если значение перед ?. равно `undefined` или `null`.
```js
let user = {}; // пользователь без адреса
alert( user?.address?.street ); // undefined (без ошибки)
```
### Другие варианты применения: ?.(), ?.[]
Опциональная цепочка ?. — это не оператор, а специальная синтаксическая конструкция, которая также работает с функциями и квадратными скобками.
Например, ?.() используется для вызова функции, которая может не существовать.
***Мы можем использовать ?. для безопасного чтения и удаления, но не для записи***
Опциональная цепочка ?. не имеет смысла в левой части присваивания.
```js
let user = null;
user?.name = "John"; // Ошибка, не работает
// то же самое что написать undefined = "John"
```
## Итого
Синтаксис опциональной цепочки ?. имеет три формы:
1. **obj?.prop** возвращает ***obj.prop*** если `obj` существует, в противном случае `undefined`.
2. **obj?.[prop]** возвращает ***obj[prop]*** если `obj` существует, в противном случае `undefined`.
3. **obj.method?.()** вызывает ***obj.method()***, если obj.method существует, в противном случае возвращает `undefined`.
Как мы видим, все они просты и понятны в использовании. ?. проверяет левую часть на `null/undefined` и позволяет продолжить вычисление, если это не так.
Цепочка ?. позволяет безопасно получать доступ к вложенным свойствам.

View File

@@ -0,0 +1,44 @@
---
sidebar_position: 6
---
# Тип данных Symbol
Символ (symbol) примитивный тип данных, использующийся для создания уникальных идентификаторов.
```js
// Создаём новый символ - id
let id = Symbol();
```
При создании, символу можно дать описание (также называемое имя), в основном использующееся для отладки кода
Символы гарантированно уникальны. Даже если мы создадим множество символов с одинаковым описанием, это всё равно будут
разные символы. Описание это просто метка, которая ни на что не влияет.
```js
let id1 = Symbol("id");
let id2 = Symbol("id");
alert(id1 == id2); // false
```
💥***Символы не преобразуются автоматически в строки***
#### Символы игнорируются циклом for…in
Свойства, чьи ключи символы, не перебираются циклом for..in.
Символы имеют два основных варианта использования:
1. «Скрытые» свойства объектов.
Если мы хотим добавить свойство в объект, который «принадлежит» другому скрипту или библиотеке, мы можем создать
символ и использовать его в качестве ключа. Символьное свойство не появится в for..in, так что оно не будет нечаянно
обработано вместе с другими. Также оно не будет модифицировано прямым обращением, так как другой скрипт не знает о
нашем символе. Таким образом, свойство будет защищено от случайной перезаписи или использования.
Так что, используя символьные свойства, мы можем спрятать что-то нужное нам, но что другие видеть не должны.
2. Существует множество системных символов, используемых внутри JavaScript, доступных как Symbol.*. Мы можем
использовать их, чтобы изменять встроенное поведение ряда объектов. Например, в дальнейших главах мы будем
использовать Symbol.iterator для итераторов, Symbol.toPrimitive для настройки преобразования объектов в примитивы и
так далее.

View File

@@ -0,0 +1,27 @@
---
sidebar_position: 7
---
# Преобразование объектов в примитивы
Преобразование объекта в примитив вызывается автоматически многими встроенными функциями и операторами, которые ожидают примитив в качестве значения.
Существует всего 3 типа (хинта) для этого:
- "string" (для alert и других операций, которым нужна строка)
- "number" (для математических операций)
- "default" (для некоторых других операторов, обычно объекты реализуют его как "number")
Спецификация явно описывает для каждого оператора, какой ему следует использовать хинт.
Алгоритм преобразования таков:
1. Сначала вызывается метод obj[Symbol.toPrimitive](hint), если он существует,
2. В случае, если хинт равен "string"
- происходит попытка вызвать obj.toString() и obj.valueOf(), смотря что есть.
3. В случае, если хинт равен "number" или "default"
- происходит попытка вызвать obj.valueOf() и obj.toString(), смотря что есть.
Все эти методы должны возвращать примитив (если определены).
На практике часто бывает достаточно реализовать только obj.toString() в качестве универсального метода для преобразований к строке, который должен возвращать удобочитаемое представление объекта для целей логирования или отладки.