Рубрики
Без рубрики

Ссылки на объект (почему [‘this’]! == [‘this’])

Изучение того, как JavaScript ссылается на массивы/объекты при назначении переменным. Tagged with JavaScript, WebDev, новички, программирование.

Если вы бежите console.log (['this'] === ['this']) В JavaScript, что бы вы ожидали увидеть? Ну, было бы совершенно рационально ожидать, что Верно был бы результатом, но вместо этого мы видим ложный Анкет Давайте посмотрим на несколько тестов:

// Control Test
console.log('this' === 'this');
// => true

// Test 1
console.log(['this'] === ['this']);
// => false

// Test 2
const arr1 = ['this'];
const arr2 = ['this'];
console.log(arr1 === arr2);
// => false

// Test 3
const arr3 = ['this'];
const arr4 = arr3;
console.log(arr3 === arr4);
// => true

Наш контрольный тест напрямую сравнить две идентичные возвраты строки Верно как и ожидалось. Первые два теста, сравнивающие, казалось бы, идентичные массивы, журнал ЛОЖЬ , но третьи журналы истинный . Так что же на самом деле здесь? Давайте посмотрим, как JavaScript назначает разные типы данных переменным.

Типы данных

Примитивный

Это потенциально неожиданное поведение будет происходить только для определенных типов данных. В JavaScript данные могут быть классифицированы как примитивные значения или объекты. Примитив Типы включают в себя строку, число, бигинт, логический, неопределенный, символ и нулевый. Когда вы назначаете примитивный тип переменной, переменная содержит само значение. Это позволяет нам сравнивать два примитивных значения и интуитивно ожидать правильного ответа.

console.log('this' === 'this');
// => true

console.log(1 === 1);
// => true

console.log(true === true);
// => true

const myString1 = 'this';
const myString2 = 'this';
console.log(myString1 === myString2);
// => true

Объекты

Неприемные типы данных ведут себя по-разному. Эти типы данных классифицируются как Объекты и включайте такие вещи, как объекты, массивы и функции: типы данных, которые хранят сбор значений. За MDN, относительно того, почему функции и массивы включены в категорию Объекты :

Функции регулярные объекты с дополнительной способностью быть Callible Анкет

Массивы регулярные объекты, для которых существует определенная связь между целочисленными свойствами и длина имущество.

Когда вы назначаете эти типы данных переменной, сама коллекция не хранится в переменной. Вместо этого хранится ссылка на коллекцию. Давайте внимательнее рассмотрим один из испытаний ранее:

const arr1 = ['this'];
const arr2 = ['this'];
console.log(arr1 === arr2);
// => false

В этом примере, когда arr1 назначается, массив ['this'] хранится где -то в памяти, а сама переменная теперь является адресом местоположения памяти. Когда arr2 Инициализируется, массив хранится в другом месте в памяти (отдельно от первого массива), и этот второй адрес хранится в переменной. С тех пор arr1 и arr2 Иметь два отдельных адреса с двумя отдельными массивами, сравнение двух переменных приведет к ложный Анкет

Давайте посмотрим на другой пример:

const arr3 = ['this'];
const arr4 = arr3;
console.log(arr3 === arr4);
// => true

Здесь мы назначаем arr3 к arr4 Анкет Делая это, обе переменные указывают на один и тот же массив в памяти. Обе переменные имеют адрес с одним и тем же массивом в памяти, поэтому сравнение двух переменных приведет к Верно Анкет

Примеры здесь охватывают массивы, но этот принцип также применяется к другим непримитивым типам данных:

const obj1 = {this: 'that'};
const obj2 = {this: 'that'};
console.log(obj1 === obj2);
// => false

const obj3 = {this: 'that'};
const obj4 = obj3;
console.log(obj3 === obj4);
// => true

const func1 = () => {};
const func2 = () => {};
console.log(func1 === func2);
// => false

const func3 = () => {};
const func4 = func3;
console.log(func3 === func4);
// => true

Разрушительные модификации

Есть еще одна важная концепция, чтобы понять, что создает тот факт, что переменные, которые хранят ссылки на объекты в памяти. Поскольку несколько переменных могут указывать на одни и те же данные в памяти, важно проявлять осторожность при создании разрушительные модификации Анкет Взгляните на этот пример:

const arr3 = ['this'];
const arr4 = arr3;
arr4[0] = 'that';
console.log(arr3);
// => ['that']
console.log(arr4);
// => ['that']

В примере оба arr3 и arr4 указывают на тот же массив в памяти. Когда элемент в arr4 Изменен, он меняет массив в памяти. Поскольку обе переменные указывают на один и тот же массив в памяти, это изменение можно увидеть путем регистрации arr3 хотя arr3 не был напрямую изменен. Этот пример напрямую изменил элемент в массиве, но важно отметить, что Многие методы массива и объектов разрушительны и изменяют исходный объект . Я рекомендую просмотреть документацию для массивы и объекты Если вам нужно знать, какие методы разрушительны.

Если вам нужно присвоить массив или объект новой переменной и внести изменения, не влияя на оригинал, вам нужно сделать копию. Если есть только один уровень данных, неглубокую копию будет достаточно и будет легко выполнить. С ES6 неглубокая копия может быть быстро создана с помощью оператора распространения ( ... ):

const arr5 = [1, 2, 3];
const arr6 = [...arr5];
console.log(arr5 === arr6);
// => false
arr6[1] = 'b';
console.log(arr5);
// => [1, 2, 3]
console.log(arr6);
// => [1, 'b', 3]

Поскольку мы сделали копию, Арр5 и arr6 Теперь укажите на два разных массива в памяти. Мы можем подтвердить это, сравнив два массива и регистрируя результат ( false ). Изменения могут быть внесены в массив, связанный с одной переменной, не влияя на другую.

Глубокие объекты

Объекты с вложенными уровнями немного сложнее. Мы все еще можем создать мелкую копию для разделения элементов верхнего уровня, но все, что вложенное внутри, будет сохранено в качестве ссылки на какой -то объект в памяти. Вот демонстрация:

const arr7 = [1, 2, [3, 4]];
const arr8 = [...arr7];
console.log(arr7 === arr8);
// => false
console.log(arr7[2] === arr8[2]);
// => true
arr8[1] = 'b';
arr8[2][1] = 'd';
console.log(arr7);
// => [1, 2, [3, 'd']]
console.log(arr8);
// => [1, 'b', [3, 'd']]

Итак, мы можем продемонстрировать, что arr7 и arr8 указывают на два разных массива с первым Консоль.log Анкет Однако, когда мы сравниваем подсолитель в индексе 2 в каждом массиве, мы обнаруживаем, что они оба указывают на один и тот же массив в памяти. Мутирующие элементы в верхнем уровне одного массива не повлияют на другое, но мутирующие элементы в суб-араме будут влиять оба . Это может быть немного запутанным, так что вот простая диаграмма:

Обе переменные указывают на другой массив на верхнем уровне, но эти два массива по -прежнему указывают на один и тот же массив для одного из элементов. Чтобы полностью разделить две наши переменные, нам нужно будет сделать глубокую копию.

Глубокое копирование

С JSON

Есть несколько способов сделать глубокую копию объекта или массива. Один из способов – использовать JSON stringify и Parse Методы:

const arr9 = [1, 2, [3, 4]];
const arr10 = JSON.parse(JSON.stringify(arr9));
console.log(arr9 === arr10);
// => false
console.log(arr9[2] === arr10[2]);
// => false
arr10[1] = 'b';
arr10[2][1] = 'd';
console.log(arr9);
// => [1, 2, [3, 4]]
console.log(arr10);
// => [1, 'b', [3, 'd']]

Это работает достаточно хорошо во многих ситуациях, но не совсем копирует все типы данных. Любой неопределенное Значения в объекте будут заменены NULL Анкет Кроме того, любой Дата Объекты будут преобразованы в строковое представление. Таким образом, скопированный массив будет полностью независимы от оригинала, но это не может быть точное копия

// undefined values are replaced with null
console.log(JSON.parse(JSON.stringify([undefined])));
// => [null]
// Date objects are replaced with the string representation
const myDate = new Date();
console.log(typeof myDate);
// => object
const myDateCopy = JSON.parse(JSON.stringify(myDate));
console.log(typeof myDateCopy);
// => string

С библиотеками

Некоторые библиотеки JavaScript предоставляют методы создания глубоких копий. Одним из примеров этого будет Лодаш Клонип метод . Если вы используете библиотеку, в которой есть такой метод, проверьте документацию, чтобы убедиться, что она работает так, как вам нужно.

С рекурсией

Вы также можете создать свою собственную функцию, чтобы сделать глубокую копию! Вот рекурсивная функция, которую я написал, чтобы сделать это:

function deepCloner(target) {
  if (Array.isArray(target)) {
    return target.map(deepCloner);
  } else if (target instanceof Date) {
    return new Date(target);
  } else if (typeof target === 'object' && target !== null) {
    const newObj = {};
    for (const key in target) {
      newObj[key] = deepCloner(target[key])
    }
    return newObj;
  }
  return target;
}

Чтобы объяснить, что он делает:

  1. Если вход является массивом, переверните через массив с карта Метод, передайте каждый элемент в DeepCloner функционируйте рекурсивно и верните новый массив.
  2. Если ввод является объектом Date, создайте копию объекта Date с Новая дата () Анкет
  3. Если вход является объектом (но не значением null ), перечислите через пары ключа/значение и перенесите значения рекурсивно в DeepCloner функция
  4. Если ввод не соответствует ни одному из вышеуказанных критериев, верните сам вход без модификации.

Я полагаю, что эта функция должна подходить для большинства ситуаций, но могут быть и другие краевые случаи, которые я еще не объяснил. Одна из таких ситуаций, о которой я могу придумать, это если ссылка на функцию хранится в исходном объекте. Глубокая копия все равно будет ссылаться на ту же функцию в памяти, хотя я не предвижу, что это проблема. Оставьте комментарий, если вы можете подумать о любых типах данных, которые это может не охватить! Я также включил в нижнюю часть этого поста, который показывает эту функцию в действии.

Вывод

То, как объекты упоминаются в переменных, может не быть интуитивно понятным для новичков в JavaScript. В первый раз, когда я заметил, что изменение элемента в массиве, связанном с одной переменной, может повлиять на другие переменные, которые я был полностью ошеломлен. Не зная, что JavaScript делает за кулисами с объектами, трудно понять, почему некоторые из этих поведений происходят. Теперь, когда я лучше понимаю, почему это происходит, мне гораздо легче писать код, чтобы избежать этого. Надеюсь, это тоже вам поможет! Спасибо за чтение!

Оригинал: “https://dev.to/fitzgeraldkd/object-references-why-thisthis-5l6”