Объект, ссылающийся на параметр функции JavaScript

Я создавал игру, похожую на agar.io, но заметил странное поведение при передаче объекта в функцию.

Я упростил код, чтобы выделить проблему. В коде я передаю объект gameData в качестве параметра в update, но когда я изменяю параметр внутри функции, он передает изменения в объект, на который имеется ссылка.

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

Я не уверен, что здесь происходит, я хотел бы понять, почему это происходит и как это обойти. Мне удалось исправить это с помощью линии

update({x: gameData.x, y: gameData.y})

но это не чистый код и не объясняет поведение.

С уважением, Каллум Живой код в jsfiddle

var gameData = {
    x: 0,
    y: 0
};

setInterval(heartbeat, 1000 /1);

function heartbeat() {
    console.info('=====================');
    update({x: gameData.x, y: gameData.y}); // doesn't update object
    update(gameData);                      // updates object
    // gameData = update(gameData);
}

function update(data) { 
    data.x += 1;
    data.y += 1;

    console.info('gameData', gameData);

    // return data;
}

Так работает JavaScript (и многие другие языки). Объект как значение на самом деле является ссылка для объекта. Представьте себе последствия для производительности, если бы объекты копировались при передаче в функции.

Pointy 05.10.2018 16:02
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
1
56
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

update({x: gameData.x, y: gameData.y});

эта строка создает новый объект, содержащий атрибуты x и y, который вы передаете своей функции update, но gameData - это глобальная переменная, с которой вы работаете. поэтому при вызове update с новым объектом, который вы только что создали, обновляет значения этого нового объекта, и они нигде не сохраняются после вызова функции.

но

update(gameData);

получает объект gameData в качестве аргумента и обновляет значения x и y объекта gameData, который ссылается на глобальную переменную, которую вы определили в верхней части кода, а в console.info(gameData) вы видите значения в этом объекте.

ЗАМЕТКА:

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

function update(data){
    return { x: data.x + 1, y: data.y + 1 }
}

и назовите это так:

gameData = update(gameData)

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

Callum 05.10.2018 19:38

@Callum, вы можете отметить ответ как правильный, если он дал вам ответ! а также обратите внимание, что использование глобальных переменных всегда опасно и может привести к серьезным проблемам, если вы пытаетесь добиться чего-то большого.

Mo Ganji 05.10.2018 21:09

Я считаю этот пример действительно интересным. Постараюсь подробнее объяснить, как это работает.

Как мы видим, наши коды дважды вызывали функцию update(), однако каким-то образом объект gameData обновляется только один раз.

Давайте посмотрим:

function heartbeat() {
    update({x: gameData.x, y: gameData.y});  // I see this as a getter
    update(gameData);                        // I see this as a setter
}

Для первого вызова мы фактически передаем примитивные значенияgameData.x и gameData.y в форме объекта. На самом деле это только примитивные значения, а не использованная литература. Любые действия, которые мы сделали с этим примитивные значения, не повлияют на фактический объект, который хранится где-то в памяти.

Для второго вызова мы передали ссылка объекта gameData, поэтому, когда вы переназначаете свойства gameData.x и gameData.y на новые значения, они напрямую влияют на фактический объект в памяти. Вот почему мы получаем новое значение этих свойств при вызове console.info(gameData).

Другие вопросы по теме