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

Причина

JS прямо сейчас имеет шесть примитивных типов: s tring, n umber, b oolean, n ull, u ndefined и s ymbol, и, когда что-то не соответствует одному из них, оно назначается как объект, как в случае с массивами и функции.

«Объекту нужны ключ и значение»

let foo = [1, 2, 3]
// Is internally for JS something of the likes of
let foo = {
  "0": 1,
  "1": 2,
  "2": 3,
}
// And that is why you can access it with
foo[0]
foo[1]
foo[2]
// If object properties/methods could be numbers you could access in this case like so
foo.0
foo.1
foo.2

Что это значит

Это может вызвать некоторые проблемы как минимум в двух сценариях: при назначении по ссылке неосознанно и при использовании for...in.

По ссылке

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

let firstObject = {
  foo: 1,
}
console.log(firstObject) // {foo: 1}
let secondObject = firstObject
secondObject.bar = 2
console.log(firstObject) // {bar: 2, foo: 1}
console.log(secondObject) // {bar: 2, foo: 1}

В то время как примитивные типы присваивают значение по значению в случае объектов, которые они назначают по ссылке, поэтому в момент, когда вы делаете let secondObject = firstObject, вы фактически говорите «всякий раз, когда secondObject изменяется, также изменяйте firstObject». Поскольку это происходит с объектами, то же самое произойдет и при использовании с массивами.

Чтобы избежать ссылок, вы можете использовать что-то вроде этого

let firstObject = {
  foo: 1
}
let secondObject = {}
for (let key in firstObject) {
  secondObject[key] = firstObject[key]
}
secondObject.bar = 2
console.log(firstObject) // {foo: 1}
console.log(secondObject) // {bar: 2, foo: 1}

Это работает, поскольку этот secondObject[key] = firstObject[key] присваивает примитивный тип, в данном случае число «1». Вы также можете добиться этого изначально, используя функцию ES6 под названием Object.assign.

let secondObject = Object.assign({}, firstObject)

Что делать, если у меня есть объект внутри объекта? - Что ж, в этом сценарии и в примере с for, и в примере с Object.assign не удастся присвоить по значению какие-либо вложенные объекты в вашем объекте, я бы рекомендовал вам использовать _.merge из lodash или $.extend из jquery. Вы также можете создать свой собственный, это может быть хорошим упражнением.

Для… в

let foo = [1, 2, 3]
foo.bar = 4
for (let key in foo) {
  console.log('--->', key) // this will output "0", "1", "2", "bar"
}

Хотя раньше вы могли ожидать, что for...in будет запускаться только один раз, на самом деле он будет повторяться четыре раза перед выходом, поскольку на самом деле у нас есть 4 свойства, а не 1, три из индексов массива и одно из нашего свойства bar.

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

Заключение

Надеюсь, это помогло вам узнать немного больше о том, как объекты и массивы работают в JS, и сэкономит драгоценное время на отладку (или избавит вас от необходимости вообще).

Статья 13 из 30, часть проекта по публикации статьи хотя бы раз в неделю, от досужих мыслей до туториалов. Оставьте комментарий, подпишитесь на меня на Диого Спинола, а затем возвращайтесь к своему блестящему проекту!