이것은 사용자가 2025-7-21 21:57에 https://www.aleksandrhovhannisyan.com/blog/javascript-pass-by-reference/을(를) 위해 저장한 이중 언어 스냅샷 페이지로, 몰입형 번역에 의해 제공된 이중 언어 지원이 있습니다. 저장하는 방법을 알아보세요?

Is JavaScript Pass by Reference?
JavaScript는 참조에 의한 전달인가요?

If you’re reading this article, chances are that you’ve heard the terms “pass by value” and “pass by reference”—whatever those mean. Maybe you were told that JavaScript is a pass-by-value language, or maybe you heard that it’s pass by reference. More commonly, you’ve probably heard that it’s both. This explanation is very common:
이 글을 읽고 있다면, 아마도 “값에 의한 전달(pass by value)”과 “참조에 의한 전달(pass by reference)”라는 용어를 들어본 적이 있을 것입니다—그 의미가 무엇이든 간에요. 누군가는 JavaScript가 값에 의한 전달 언어라고 들었을 수도 있고, 또 누군가는 참조에 의한 전달이라고 들었을 수도 있습니다. 더 흔하게는, 두 가지 모두라고 들었을 가능성이 큽니다. 이런 설명이 매우 흔합니다:

Primitives are passed by value; objects are passed by reference.
원시 값은 값에 의한 전달되고, 객체는 참조에 의한 전달된다.

This sounds simple enough to be true, but it’s actually not. JavaScript, like most programming languages, is a pass-by-value language and does not support pass by reference, even though it does have what we call “references” (object references).
이 설명은 충분히 간단해서 사실일 것 같지만, 실제로는 그렇지 않습니다. JavaScript는 대부분의 프로그래밍 언어처럼 값에 의한 전달 언어이며, 참조에 의한 전달을 지원하지 않습니다. 비록 “참조”(객체 참조)라고 부르는 것이 있긴 하지만요.

To make matters more confusing, there’s a lot of misinformation out there about this topic. It’s something that developers struggle with not only in JavaScript but also in other languages. Here’s what the Medium article linked above says about passing by reference:
상황을 더 혼란스럽게 만드는 것은 이 주제에 대해 잘못된 정보가 많이 퍼져 있다는 점입니다. 이는 개발자들이 JavaScript뿐만 아니라 다른 언어에서도 어려워하는 부분입니다. 위에 링크된 Medium 기사에서는 참조에 의한 전달에 대해 다음과 같이 설명하고 있습니다:

Changing the argument inside the function affect the variable passed from outside the function. In Javascript objects and arrays follows pass by reference.
함수 내부에서 인수를 변경하면 함수 외부에서 전달된 변수에 영향을 미칩니다. JavaScript에서 객체와 배열은 참조에 의한 전달을 따릅니다.

Even the highest rated answer on StackOverflow for “Is JavaScript a pass-by-reference or pass-by-value language?”—with nearly three times more upvotes than the correct answer—misses the mark, claiming that JavaScript is a special case when it really isn’t.
“JavaScript가 참조에 의한 전달인가 값에 의한 전달인가?”라는 질문에 대해 StackOverflow에서 가장 높은 평가를 받은 답변조차도—정답보다 거의 세 배나 많은 추천을 받았음에도—JavaScript가 특별한 경우라고 주장하며 잘못된 결론을 내리고 있습니다.

In reality, only a few programming languages actually support passing arguments by reference. Two such languages are C++ and PHP. Other languages—like C, Java, JavaScript, Python, and Go, to name a few that come to mind—pass arguments by value.
실제로는, 인수를 참조에 의해 전달하는 것을 지원하는 프로그래밍 언어는 몇 안 됩니다. 그 중 두 가지가 C++과 PHP입니다. C, Java, JavaScript, Python, Go 등 다른 언어들은 인수를 값에 의한 전달로 처리합니다.

So why do so many people get this wrong? And does it even matter? That’s what we’re here to learn! If you’d like the short answer, feel free to jump to the section titled Passing Pointers (“References”) by Value.
그렇다면 왜 이렇게 많은 사람들이 이것을 잘못 이해할까요? 그리고 이것이 정말 중요한가요? 바로 그 점을 알아보려 합니다! 간단한 답변을 원하신다면, '값에 의한 포인터("참조") 전달' 섹션으로 바로 넘어가셔도 좋습니다.

Table of Contents
목차

Prerequisite Terminology
필수 용어

Before we move on, we’ll need a quick terminology primer. Consider this code:
계속하기 전에 간단한 용어 설명이 필요합니다. 다음 코드를 살펴보세요:

javascript code snippet
function doSomething(arg) {
    // ...
}

const obj = { foo: 'bar' };
doSomething(obj);

A formal parameter (parameter for short) is the local variable declared in a function’s signature. Above, the formal parameter of doSomething is arg. On the other hand, the argument is the variable (obj) whose value gets passed into the function.
형식 매개변수(줄여서 매개변수)는 함수 시그니처에 선언된 지역 변수입니다. 위 예제에서 doSomething 의 형식 매개변수는 arg 입니다. 반면에 인수는 함수에 전달되는 값이 담긴 변수( obj )입니다.

When your browser executes this code, it will create two “frames” in its memory stack: one for the main (enclosing) scope and and another for the doSomething invocation. Each frame is like a compartment in a drawer, storing values and objects that are not accessible from within other compartments. Diagrammatically, here’s what the stack would look like once doSomething has been called:
브라우저가 이 코드를 실행하면 메인(포함) 스코프용과 doSomething 호출용 두 개의 “프레임”이 메모리 스택에 생성됩니다. 각 프레임은 서랍 속 칸과 같아서 다른 칸에서는 접근할 수 없는 값과 객체를 저장합니다. 도식적으로, doSomething 이 호출된 후 스택은 다음과 같이 보일 것입니다:

Two stack frames are shown in memory; one corresponds to the main method. The other, positioned directly above, is a function that was called from main. That stack frame contains the argument that was passed along when the function was invoked.

With that out of the way, let’s get into the meat of the discussion.
이제 그 부분은 넘어가고, 본격적인 논의로 들어가 보겠습니다.

JavaScript Doesn’t Pass by Reference
JavaScript는 참조에 의한 전달을 하지 않는다

If there’s one thing you should take away from this article, it’s this: Passing a reference into a function is not the same as passing an argument by reference. This muddied terminology is the main source of confusion in understanding pass by value vs. pass by reference in JavaScript and other languages. It’s something that trips up many developers.
이 글에서 꼭 기억해야 할 한 가지는 다음과 같습니다: 함수에 참조를 전달하는 것과 인수를 참조에 의한 전달(pass by reference)하는 것은 같지 않다는 점입니다. 이 혼란스러운 용어 사용이 JavaScript 및 다른 언어에서 값에 의한 전달과 참조에 의한 전달을 이해하는 데 가장 큰 혼란의 원인입니다. 많은 개발자들이 이 부분에서 헷갈립니다.

In JavaScript, variables that store objects are called object references (usually “references” for short). The term reference is a natural way to talk about these variables in English because we say that a variable refers to an object in memory, sort of like how an article might reference a source.
JavaScript에서 객체를 저장하는 변수는 객체 참조(object references)라고 불리며(보통은 간단히 “참조”라고 합니다), 참조라는 용어는 영어에서 이러한 변수를 설명하는 자연스러운 방식입니다. 변수는 메모리 내의 객체를 참조한다고 말하는데, 이는 마치 글이 출처를 참조하는 것과 비슷합니다.

An object resides somewhere in memory. A little black square is shown, representing a variable named person that points to a specific memory address.

There’s nothing wrong with using the term “reference” to talk about objects in this manner, and I don’t want to discourage you from doing so. In JavaScript, this is the accepted terminology; if you choose to deviate from it, you’ll end up confusing others.
객체에 대해 이런 식으로 “참조”라는 용어를 사용하는 데는 아무런 문제가 없으며, 그렇게 하는 것을 막고 싶지 않습니다. JavaScript에서는 이것이 일반적으로 사용되는 용어입니다; 만약 이를 벗어나서 표현한다면 다른 사람들을 혼란스럽게 만들 수 있습니다.

But this terminology is also confusing. Who can blame developers for making the aforementioned connection? It’s only natural to think that a language with references must support passing by reference. To suggest otherwise may even seem absurd or pedantic.
하지만 이 용어도 혼란을 줍니다. 앞서 언급한 연결을 개발자들이 하는 것을 누가 탓할 수 있겠습니까? 참조가 있는 언어라면 반드시 참조에 의한 전달을 지원한다고 생각하는 것이 당연합니다. 그렇지 않다고 말하는 것은 심지어 터무니없거나 지나치게 엄격하게 보일 수도 있습니다.

So, to clear up this confusion, we’ll need to understand two things:
그래서 이 혼란을 해소하기 위해 두 가지를 이해해야 합니다:

  1. What the “reference” in “pass by reference” really means.
    “참조에 의한 전달”에서 “참조”가 실제로 의미하는 바가 무엇인지.
  2. The difference between pass by value and pass by reference in JavaScript.
    JavaScript에서 값에 의한 전달과 참조에 의한 전달의 차이점.

True References Are Aliases
진정한 참조는 별칭입니다

Let’s clarify one thing before moving on: The word “reference” in “pass by reference” has a special meaning, and it does not mean “object reference.” JavaScript does not have these “true” references. Again, terminology can be confusing!
진행하기 전에 한 가지를 명확히 하겠습니다: “참조에 의한 전달(pass by reference)”에서 “참조(reference)”라는 단어는 특별한 의미를 가지며, “객체 참조(object reference)”를 의미하지 않습니다. JavaScript에는 이러한 “진정한” 참조가 없습니다. 다시 말하지만, 용어가 혼동을 줄 수 있습니다!

JavaScript—like Java, Python, and other programming languages—uses the term “reference” when talking about variables that point to objects in memory. But as I noted above, while this makes perfect sense in English, it makes it very confusing to talk about “passing references” without losing your mind.
JavaScript는 Java, Python 및 기타 프로그래밍 언어와 마찬가지로 메모리 내 객체를 가리키는 변수에 대해 이야기할 때 “참조”라는 용어를 사용합니다. 하지만 앞서 언급했듯이, 영어에서는 완벽하게 이해되지만 “참조 전달”에 대해 이야기할 때 혼란스러워질 수 있습니다.

In a class on programming language theory, you’ll learn that the “reference” in “pass by reference” is actually something quite different from these “object references” that you’re used to hearing about. In fact, a true reference has nothing to do with objects—references can either hold primitive values or objects.
프로그래밍 언어 이론 수업에서, “참조에 의한 전달(pass by reference)”의 “참조(reference)”는 여러분이 익숙하게 들어온 “객체 참조(object references)”와는 실제로 매우 다르다는 것을 배우게 됩니다. 사실, 진정한 참조는 객체와는 아무런 관련이 없으며—참조는 원시 값이나 객체를 모두 가질 수 있습니다.

A true reference variable is an alias, or another name for an object. If you’ve mainly worked with languages like C, Java, JavaScript, Python, and others up until now, this notion of aliases may seem a bit foreign to you. And that’s because those languages don’t have references! For this reason, I’ll use C++ to demonstrate how true references work, contrasting them with the behavior of object references in JavaScript.
진정한 참조 변수는 별칭(alias), 즉 객체의 또 다른 이름입니다. 지금까지 주로 C, Java, JavaScript, Python 등과 같은 언어를 다뤄왔다면, 이러한 별칭 개념이 다소 낯설게 느껴질 수 있습니다. 그 이유는 이들 언어에는 참조가 없기 때문입니다! 이런 이유로, 저는 C++를 사용하여 진정한 참조가 어떻게 작동하는지 보여주고, 이를 JavaScript의 객체 참조 동작과 대조해 설명할 것입니다.

The following C++ code demonstrates true reference variables in action:
다음 C++ 코드는 진정한 참조 변수의 동작을 보여줍니다:

cpp code snippet
#include <iostream>
#include <string>

int main()
{
    // An ordinary variable
    std::string myName = "Aleksandr";

    // A reference variable that aliases myName
    std::string &myNickname = myName;

    // let's change myNickname to store a different string
    myNickname = "Alex";

    // the change is reflected in both variables!
    std::cout << myName << std::endl; // "Alex"
    std::cout << myNickname << std::endl; // "Alex"
}

In this example, I created a simple string and bound it to the variable myName; then, I created another variable that serves as an alias for myName by using the special ampersand syntax for declaring references. I then bound a new string value to this alias. The change was reflected not only in the alias but also in the original (aliased) variable.
이 예제에서는 간단한 문자열을 생성하여 변수 myName 에 할당했습니다. 그런 다음 참조를 선언할 때 사용하는 특수 앰퍼샌드(&) 구문을 사용하여 myName 의 별칭 역할을 하는 또 다른 변수를 만들었습니다. 이후 이 별칭에 새로운 문자열 값을 할당했습니다. 이 변경은 별칭뿐만 아니라 원래(별칭이 지정된) 변수에도 반영되었습니다.

In languages that don’t support reference variables (e.g., JavaScript), two variables may share a copy of a value, but it’s guaranteed that those variables will occupy different memory addresses. Here’s an attempt at the above in JavaScript:
참조 변수를 지원하지 않는 언어(예: JavaScript)에서는 두 변수가 값의 복사본을 공유할 수 있지만, 해당 변수들이 서로 다른 메모리 주소를 차지한다는 것은 보장됩니다. 다음은 JavaScript에서 위 내용을 시도한 예입니다:

javascript code snippet
let myName = "Aleksandr";
let myNickname = myName;

myNickname = "Alex";

console.log(myName); // "Aleksandr"
console.log(myNickname); // "Alex"

On the second line, we’re creating a copy of the string "Aleksandr" and binding it to a new variable named myNickname. Importantly, these two variables occupy different memory addresses—assigning a new value to one variable does not affect the value referred to by the other variable.
두 번째 줄에서는 문자열 "Aleksandr" 의 복사본을 생성하여 myNickname 이라는 새 변수에 바인딩하고 있습니다. 중요한 점은 이 두 변수가 서로 다른 메모리 주소를 차지한다는 것이며, 한 변수에 새 값을 할당해도 다른 변수가 참조하는 값에는 영향을 주지 않는다는 것입니다.

Unlike a variable that receives a copy of a value, a reference variable does not have its own memory address—it shares the same exact memory address as the original variable, as if we had never declared two variables in the first place. We can verify this in C++ using something called the address-of operator (&), which returns the memory address of a variable:
값의 복사본을 받는 변수와 달리, 참조 변수는 자체 메모리 주소가 없으며 원래 변수와 정확히 동일한 메모리 주소를 공유합니다. 마치 처음부터 두 변수를 선언하지 않은 것과 같습니다. C++에서는 주소 연산자( & )를 사용하여 변수의 메모리 주소를 확인할 수 있습니다:

cpp code snippet
#include <iostream>
#include <string>

int main()
{
    std::string myName = "Aleksandr";
    std::string &myNickname = myName;

    // Print the memory address of myName
    std::cout << &myName << std::endl;

    // Print the memory address of myNickname
    std::cout << &myNickname << std::endl;
}

If you run this code, you’ll see that the same exact memory address is logged twice.
이 코드를 실행하면 동일한 메모리 주소가 두 번 출력되는 것을 확인할 수 있습니다.

It’s here that some developers claim that JavaScript does have true references, using objects to prove their point:
일부 개발자들은 JavaScript가 진정한 참조를 가지고 있다고 주장하며, 객체를 사용해 그들의 주장을 입증하려 합니다:

javascript code snippet
let me = { name: 'Aleksandr' };
let alias = me;

alias.name = 'Alex';

console.log(me); // { name: 'Alex' }
console.log(alias); // { name: 'Alex' }

This certainly seems to work the way references behave, but it’s not the same at all. What we’ve done here can be broken down into four simple steps:
이것은 확실히 참조가 작동하는 방식처럼 보이지만, 전혀 동일하지 않습니다. 여기서 우리가 한 작업은 네 가지 간단한 단계로 나눌 수 있습니다:

  1. We’ve allocated space in memory to store an object ({ name: 'Aleksandr' }).
    객체( { name: 'Aleksandr' } )를 저장할 메모리 공간을 할당했습니다.
  2. We’ve created a variable named me that points to the memory address where this object resides.
    이 객체가 위치한 메모리 주소를 가리키는 me 이라는 변수를 생성했습니다.
  3. We’ve created yet another variable, alias, that also points to the same memory address as me.
    우리는 me 과 동일한 메모리 주소를 가리키는 또 다른 변수 alias 를 만들었습니다.
  4. We can use either variable to modify the object at that memory address. Since both variables point to the same memory address, any changes to the object at that location will be reflected when we poll either variable in the future.
    우리는 두 변수 중 어느 것을 사용해도 해당 메모리 주소에 있는 객체를 수정할 수 있습니다. 두 변수 모두 같은 메모리 주소를 가리키기 때문에, 그 위치에 있는 객체에 대한 모든 변경 사항은 앞으로 두 변수 중 어느 하나를 조회할 때 반영됩니다.

But these two variables, me and alias, do not share the same memory address on the stack. In other words, alias is not really a true reference. We can easily prove this by creating a new object and pointing alias to it:
하지만 이 두 변수 mealias 는 스택에서 같은 메모리 주소를 공유하지 않습니다. 다시 말해, alias 는 진정한 참조가 아닙니다. 새로운 객체를 만들고 alias 를 그것에 가리키게 하여 이것을 쉽게 증명할 수 있습니다:

javascript code snippet
let me = { name: 'Aleksandr' };
let alias = me;
alias = { name: 'Alex' };

console.log(me); // { name: 'Aleksandr' }
console.log(alias); // { name: 'Alex' }

If alias were in fact a true reference, then the change on line three would’ve been reflected in the original (aliased) variable. But it wasn’t!
만약 alias 가 진정한 참조였다면, 세 번째 줄에서의 변경 사항이 원래 (별칭이 붙은) 변수에 반영되었을 것입니다. 하지만 그렇지 않았습니다!

“Object References” Are Pointers
“객체 참조”는 포인터입니다

You may have noticed that I used the term “point” quite a lot. What’s up with that? What—pardon the awful pun—is the point I’m trying to get across?
제가 “포인트”라는 용어를 꽤 자주 사용한 것을 눈치채셨을 겁니다. 그게 무슨 뜻일까요? 제가 전달하려는 핵심—끔찍한 말장난을 용서해 주세요—이 무엇인지 말입니다.

xkcd comic about pointers. Two stick figures are depicted; one is sitting cross-legged in front of a TV playing video games, and the other is standing beside them. Player: 'Man, I suck at this game. Can you give me a few pointers?' Friend: '0x3A28213A 0x6339392C, 0x7363682E.' Player: 'I hate you.
Source: xkcd.  출처: xkcd.

So far, we’ve seen that JavaScript does not have “true” references—the ones that are used in “pass by reference”—even though that’s the accepted shorthand terminology when talking about “object references.” So what exactly are object references in JavaScript?
지금까지 우리는 JavaScript에 “참조에 의한 전달”에 사용되는 “진짜” 참조가 없다는 것을 보았습니다—비록 “객체 참조”에 대해 이야기할 때 통용되는 약식 용어이긴 하지만요. 그렇다면 JavaScript에서 객체 참조란 정확히 무엇일까요?

“Object references” in JavaScript are really pointers. A pointer is a variable that—instead of directly storing a primitive value like an int, bool, float, or char—stores the memory address where some data lives. Pointers refer to their data indirectly, using the addresses where those data can be found.
JavaScript에서 “객체 참조”는 실제로 포인터입니다. 포인터는 int , bool , float , char 과 같은 원시 값을 직접 저장하는 대신, 어떤 데이터가 저장된 메모리 주소를 저장하는 변수입니다. 포인터는 해당 데이터가 위치한 주소를 사용하여 간접적으로 데이터를 참조합니다.

An analogy may help to illustrate this: A pointer is like keeping record of a person’s home address instead of directly logging that person’s information somewhere. If you later visit that address, you might find data living there: a person with a name, age, and so forth. The person at that address may change their name, grow older, or decide to leave the property and be replaced by another tenant with an entirely different name, age, and so on. The address may even be empty and on the market. As long as we point to the same address, we can visit it to find out what data (person) lives there. We may also one day decide to point to a completely different home address with different residents or, if we’re the landlord, evict someone from their home.
비유를 통해 설명하자면, 포인터는 사람의 정보를 직접 기록하는 대신 그 사람의 집 주소를 기록하는 것과 같습니다. 나중에 그 주소를 방문하면 이름, 나이 등과 같은 데이터가 거기에 있을 수 있습니다. 그 주소에 사는 사람은 이름을 바꾸거나 나이가 들거나 집을 떠나고 완전히 다른 이름과 나이의 새로운 세입자로 교체될 수도 있습니다. 주소가 비어 있거나 매물로 나와 있을 수도 있습니다. 우리가 같은 주소를 가리키는 한, 그 주소를 방문하여 어떤 데이터(사람)가 거주하는지 알 수 있습니다. 또한 언젠가는 완전히 다른 거주자가 있는 다른 집 주소를 가리키기로 결정할 수도 있고, 집주인이라면 누군가를 집에서 내보낼 수도 있습니다.

It may not be immediately obvious why we need pointers in the first place and how they’re even relevant in JavaScript. This deserves some elaboration. If you’re already familiar with pointers, feel free to skip forward to the section on pass by value vs. pass by reference.
우리가 처음에 왜 포인터가 필요한지, 그리고 그것이 JavaScript에서 어떻게 관련되는지 즉시 명확하지 않을 수 있습니다. 이에 대해 좀 더 자세한 설명이 필요합니다. 이미 포인터에 익숙하다면 값에 의한 전달과 참조에 의한 전달 섹션으로 바로 넘어가도 좋습니다.

How Are Objects Stored in Memory?
객체는 메모리에 어떻게 저장되나요?

Computers are good at one thing: Storing, retrieving, and manipulating numbers. So what happens when we want to create compound data like objects, arrays, or strings in a programming language? How is that data stored if, for example, we have no “person” or “array” or “string” data type and computers can only store and retrieve numbers?
컴퓨터는 한 가지에 능숙합니다: 숫자를 저장하고, 검색하며, 조작하는 것. 그렇다면 프로그래밍 언어에서 객체, 배열, 문자열과 같은 복합 데이터를 만들고 싶을 때는 어떻게 될까요? 예를 들어, “person”이나 “array” 또는 “string” 데이터 타입이 없고 컴퓨터가 오직 숫자만 저장하고 검색할 수 있다면, 그 데이터는 어떻게 저장될까요?

Let’s consider this JavaScript code as an example:
예제로 이 JavaScript 코드를 살펴봅시다:

javascript code snippet
const n = 42;
const bob = { name: 'Bob' };

At the hardware level, we can store the primitive integer 42 in a CPU register and save it to the stack directly, instead of having to remember the memory address where we allocated space for this integer. Remember: Numbers are the language of computers. As it so happens, 42 is a number. Everything works out, and we store this number directly.
하드웨어 수준에서, 원시 정수 42 를 CPU 레지스터에 저장하고 스택에 직접 저장할 수 있습니다. 이 정수를 위해 할당한 메모리 주소를 기억할 필요 없이 말이죠. 기억하세요: 숫자는 컴퓨터의 언어입니다. 마침 42 은 숫자입니다. 모든 것이 잘 맞아떨어지고, 우리는 이 숫자를 직접 저장합니다.

But what if our variable refers to a more complex object (think “composite data”), like an object, array, or string? Then we need to use a pointer to remember where exactly in memory that data was stored. Because at the CPU level, we have no notion of “objects,” “arrays,” or “strings”—we can only allocate blocks of memory and store raw numbers in those addresses. If you want to store composite data in memory, you need to use a pointer to remember where that composite chunk of data begins.
하지만 만약 우리의 변수가 객체, 배열, 문자열과 같은 더 복잡한 객체(즉, “복합 데이터”)를 참조한다면 어떻게 될까요? 그럴 때는 해당 데이터가 메모리의 정확히 어디에 저장되어 있는지 기억하기 위해 포인터를 사용해야 합니다. CPU 수준에서는 “객체”, “배열”, “문자열”이라는 개념이 없기 때문입니다—우리는 단지 메모리 블록을 할당하고 그 주소에 원시 숫자를 저장할 수 있을 뿐입니다. 복합 데이터를 메모리에 저장하려면, 그 복합 데이터 덩어리가 시작하는 위치를 기억하기 위해 포인터를 사용해야 합니다.

For example, suppose that people in our world have more attributes:
예를 들어, 우리 세계의 사람들이 더 많은 속성을 가지고 있다고 가정해 봅시다:

javascript code snippet
const bob = { name: 'Bob', age: 42, country: 'USA' };

How is it possible to cram all of this data into a single memory address? There’s really no way to store { name: 'Bob', age: 42, country: 'USA' } in one memory address as-is and call it a day because { name: 'Bob', age: 42, country: 'USA' } is not a literal primitive value that a computer understands. So instead, we start at a particular memory address—called a base memory address—and begin storing these pieces of information in consecutive blocks. Then, bob points to that base memory address, such that we can work our way up later on and “reassemble” the pieces of information that we need, so to speak.
이 모든 데이터를 단일 메모리 주소에 어떻게 담을 수 있을까요? { name: 'Bob', age: 42, country: 'USA' } 을 있는 그대로 한 메모리 주소에 저장하고 끝내는 것은 사실상 불가능합니다. 왜냐하면 { name: 'Bob', age: 42, country: 'USA' } 은 컴퓨터가 이해하는 리터럴 원시 값이 아니기 때문입니다. 그래서 대신 특정 메모리 주소—기본 메모리 주소라고 부르는—에서 시작하여 이 정보 조각들을 연속된 블록에 저장하기 시작합니다. 그런 다음, bob 가 그 기본 메모리 주소를 가리키게 하여 나중에 필요한 정보 조각들을 다시 “조립”할 수 있도록 하는 것입니다.

That takes care of objects, but what about strings? They may seem like a primitive data type only because words are the basic building blocks of the English language. But remember: Computers don’t understand English, French, or Chinese; they’re just concerned with numbers. In reality, a string consists of characters that are stored in consecutive memory addresses and encoded as numbers using the Unicode standard. In programming languages, a string is implemented as a pointer to the memory address of its first character. If we know the specific character encoding that a string uses (e.g., UTF-16 in JavaScript), we can use pointer arithmetic under the hood to figure out what the string is since we already have its base memory address and the number of characters that were stored.
객체는 이렇게 처리했는데, 문자열은 어떨까요? 문자열은 영어 단어가 기본 구성 요소이기 때문에 원시 데이터 타입처럼 보일 수 있습니다. 하지만 기억하세요: 컴퓨터는 영어, 프랑스어, 중국어를 이해하지 못하며, 오직 숫자에만 관심이 있습니다. 실제로 문자열은 연속된 메모리 주소에 저장된 문자들로 구성되며, 유니코드 표준을 사용해 숫자로 인코딩됩니다. 프로그래밍 언어에서 문자열은 첫 번째 문자의 메모리 주소를 가리키는 포인터로 구현됩니다. 만약 문자열이 사용하는 특정 문자 인코딩(예: JavaScript의 UTF-16)을 알고 있다면, 기본 메모리 주소와 저장된 문자 수를 이미 알고 있으므로 내부적으로 포인터 산술을 사용해 문자열이 무엇인지 알아낼 수 있습니다.

The key takeaway here is that pointers are a fundamental tool in programming that exists in the implementation of nearly every language. Whether they are hidden from plain sight or made explicit by a special syntax is not important. What matters is that you understand how memory works and that most languages do not have “references.”
여기서 핵심은 포인터가 거의 모든 언어 구현에 존재하는 프로그래밍의 기본 도구라는 점입니다. 포인터가 눈에 보이지 않게 숨겨져 있든, 특별한 문법으로 명시되어 있든 중요하지 않습니다. 중요한 것은 메모리가 어떻게 작동하는지 이해하는 것이며, 대부분의 언어에는 “참조”가 없다는 사실입니다.

Pass by Value vs. Pass by Reference
값에 의한 전달 vs. 참조에 의한 전달

Okay, we’ve established that “object references” in JavaScript are really pointers and that pointers are good at one thing: storing memory addresses. Objects reside at these memory addresses. We can visit those addresses and modify the objects that live there, or we can have a variable point to an entirely new object at some other location in memory. With this background, we’re ready to understand why pass by reference does not exist in JavaScript.
좋습니다, 우리는 JavaScript에서 "객체 참조"가 실제로 포인터이며, 포인터는 한 가지에 능숙하다는 것을 확인했습니다: 메모리 주소를 저장하는 것. 객체는 이 메모리 주소에 존재합니다. 우리는 그 주소를 방문하여 그곳에 있는 객체를 수정할 수 있고, 또는 변수가 메모리의 다른 위치에 있는 완전히 새로운 객체를 가리키게 할 수도 있습니다. 이러한 배경 지식을 바탕으로, JavaScript에는 참조에 의한 전달(pass by reference)이 존재하지 않는 이유를 이해할 준비가 되었습니다.

Pass by Reference  참조에 의한 전달

We’ve learned what references really are: aliases. And as it turns out, this is precisely the kind of reference that we’re talking about when we say a language has “pass by reference.” It’s all about aliasing arguments that were passed in!
우리는 참조가 실제로 무엇인지 배웠습니다: 별칭(alias)입니다. 그리고 알고 보니, 이것이 바로 언어가 "참조에 의한 전달"을 가진다고 할 때 말하는 참조의 종류입니다. 전달된 인수에 별칭을 붙이는 것에 관한 이야기입니다!

First, let’s understand what pass by reference does not mean: Pass by reference does not mean, “We can pass in an object to a function, modify that object, and then observe the modified result even after the function returns.”
먼저, 참조에 의한 전달이 의미하지 않는 것을 이해해 봅시다: 참조에 의한 전달은 "함수에 객체를 전달하고, 그 객체를 수정한 후 함수가 반환된 이후에도 수정된 결과를 관찰할 수 있다"는 뜻이 아닙니다.

In pass by reference, the formal parameter is a reference variable (alias) for the argument. It’s almost like two functions have temporarily agreed to call a single object by two names (one original and one alias) and to pretend that the two are equivalent. Or, in less accurate terms, you can think of it like a function “borrowing” a variable from another stack frame but calling it something else.
참조에 의한 전달(pass by reference)에서는 형식 매개변수가 인수에 대한 참조 변수(별칭)입니다. 마치 두 함수가 일시적으로 하나의 객체를 두 개의 이름(원래 이름과 별칭)으로 부르기로 합의하고, 두 이름이 동일한 것처럼 가장하는 것과 같습니다. 또는 덜 정확하게 말하면, 함수가 다른 스택 프레임에서 변수를 “빌려와서” 다른 이름으로 부르는 것과 비슷하다고 생각할 수 있습니다.

A Litmus Test for Pass by Reference
참조에 의한 전달을 위한 리트머스 테스트

There’s a classic litmus test to check if a language supports passing by reference: whether you can swap two numbers by passing them in to a function (the following code is written in JavaScript):
언어가 참조에 의한 전달을 지원하는지 확인하는 고전적인 리트머스 테스트가 있습니다: 두 숫자를 함수에 전달하여 서로 교환할 수 있는지 여부입니다(다음 코드는 JavaScript로 작성됨):

javascript code snippet
function swap(a, b) {
    const temp = a;
    a = b;
    b = temp;

    console.log(a); // 2
    console.log(b); // 4
}

let x = 4;
let y = 2;
swap(x, y);

console.log(x); // 4
console.log(y); // 2

If you run this JavaScript, you’ll find that x and y do not swap, whereas a and b do locally. Therefore, JavaScript doesn’t support passing by reference. If it did, then a would’ve been an alias for x and b would’ve been an alias for y. Any changes to a and b would have been reflected back to x and y, respectively. But they weren’t!
이 JavaScript를 실행하면 xy 은 교환되지 않고, ab 만 지역적으로 교환되는 것을 알 수 있습니다. 따라서 JavaScript는 참조에 의한 전달을 지원하지 않습니다. 만약 지원했다면, ax 의 별칭이었을 것이고, by 의 별칭이었을 것입니다. ab 에 대한 모든 변경 사항은 각각 xy 에 반영되었을 것입니다. 하지만 그렇지 않았습니다!

So, what happened here? The variables a and b are local to the stack frame of swap and receive copies of the values of the arguments that are passed in. This means that the values of a, b and x, y coincide because they are copies, not because they’re aliased.
자, 여기서 무슨 일이 일어났을까요? 변수 abswap 의 스택 프레임에 지역적으로 존재하며, 전달된 인수들의 값 복사본을 받습니다. 이는 a, bx, y 의 값이 일치하는 이유가 참조가 공유되어서가 아니라 복사본이기 때문임을 의미합니다.

In contrast, here’s an example of swapping two integers by reference in C++:
반면에, 다음은 C++에서 두 정수를 참조로 교환하는 예제입니다:

cpp code snippet
#include <iostream>

 void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
 }

int main()
{
    int x = 5;
    int y = 6;

    // Success! x = 6, y = 5
    swap(x, y);
}

Let’s break this down: x and y are the arguments that we pass into swap. The method signature of swap defines two reference parameters, a and b. Since these are reference variables, they serve as aliases for whatever two variables are passed in as arguments (x and y, respectively). Thus, the swap succeeds.
이것을 분해해 봅시다: xyswap 에 전달하는 인수입니다. swap 의 메서드 시그니처는 두 개의 참조 매개변수인 ab 를 정의합니다. 이들은 참조 변수이기 때문에, 각각 인수로 전달된 두 변수( xy )의 별칭 역할을 합니다. 따라서, 스왑이 성공합니다.

Pass by Value  값에 의한 전달

In the simplest terms, passing by value is about copying. We already saw this above with the failed JavaScript swap:
가장 간단히 말해, 값에 의한 전달은 복사에 관한 것입니다. 위에서 실패한 JavaScript 스왑 예제에서 이미 이를 보았습니다.

javascript code snippet
function swap(a, b) {
    const temp = a;
    a = b;
    b = a;

    console.log(a);
    console.log(b);
}

let x = 4;
let y = 2;
swap(x, y);

console.log(x);
console.log(y);

When an argument is passed by value, the formal parameter receives a copy of the argument’s value. The formal parameter and the argument are two different variables that just happen to share the same value. The formal parameter is a local variable on the function’s stack frame. The argument we passed in is a variable residing at a completely different memory address, in the caller’s stack frame.
인수가 값에 의해 전달될 때, 형식 매개변수는 인수 값의 복사본을 받습니다. 형식 매개변수와 인수는 같은 값을 공유하는 서로 다른 두 변수입니다. 형식 매개변수는 함수의 스택 프레임에 있는 지역 변수입니다. 우리가 전달한 인수는 호출자의 스택 프레임에 완전히 다른 메모리 주소에 위치한 변수입니다.

Passing Pointers (“References”) by Value
포인터(“참조”)를 값으로 전달하기

Now comes the most important discussion, and the key to understanding why JavaScript is not pass by reference: What happens when we pass “object references” (pointers) by value?
이제 가장 중요한 논의가 시작되며, JavaScript가 참조에 의한 전달이 아닌 이유를 이해하는 핵심입니다: “객체 참조”(포인터)를 값에 의해 전달할 때 무슨 일이 일어날까요?

javascript code snippet
function changeName(person) {
    person.name = 'Jane';
}

let bob = { name: 'Bob' };
changeName(bob);

First, let’s note again that when we pass something by value, the formal parameter receives a copy of the argument’s value.
먼저, 다시 한 번 값에 의해 무언가를 전달할 때 형식 매개변수가 인수 값의 복사본을 받는다는 점을 주목합시다.

If we’re passing a pointer into a function as an argument, what “value” does the formal parameter receive? Remember: Pointers are nothing fancy—they’re just variables that store the memory address of some data.
함수에 포인터를 인수로 전달할 때, 형식 매개변수는 어떤 “값”을 받는가? 기억하자: 포인터는 특별한 것이 아니다—그저 어떤 데이터의 메모리 주소를 저장하는 변수일 뿐이다.

So, when we pass a pointer into a function as an argument, the formal parameter receives a copy of the memory address to which the argument was pointing. We essentially end up having two different variables, on two different stack frames (caller and callee), that point to the same location in memory.
따라서 포인터를 함수에 인수로 전달하면, 형식 매개변수는 인수가 가리키던 메모리 주소의 복사본을 받는다. 결국 우리는 서로 다른 두 스택 프레임(호출자와 피호출자)에 있는 두 개의 서로 다른 변수가 동일한 메모리 위치를 가리키게 된다.

Left: stack. Right: heap. Stack contains two stack frames: main (with local variable bob) and changeName (local variable person). Both variables point to the same memory address on the heap for an object.

At that memory address is an object. We can follow either the formal parameter pointer or the argument pointer and visit that memory address to modify the object located there. In the case of the code above, we’re reading the formal parameter pointer:
그 메모리 주소에는 객체가 있다. 우리는 형식 매개변수 포인터나 인수 포인터 중 어느 쪽을 따라가도 그 메모리 주소를 방문하여 그곳에 있는 객체를 수정할 수 있다. 위 코드의 경우, 우리는 형식 매개변수 포인터를 읽고 있다.

Left: stack. Right: heap. Stack contains two stack frames: main (with local variable bob) and changeName (local variable person). Both variables point to the same memory address on the heap for an object. Sample code shows changeName modifying the name property of the object at this memory address. Text reads: "person here. I'd like to change the name property of whatever object resides at this address. bob here... still pointing to the same location in memory. I wonder if the object has changed since I last saw it?"

What we cannot do is have the formal parameter point to a different location in memory and expect the argument to point to that new location once the function returns. This would be the case if the argument were passed in by reference. But JavaScript is pass by value!
우리가 할 수 없는 것은 형식 매개변수가 다른 메모리 위치를 가리키도록 하고 함수가 반환된 후에 인수가 그 새로운 위치를 가리키기를 기대하는 것이다. 이것은 인수가 참조에 의해 전달된 경우에 해당한다. 하지만 JavaScript는 값에 의한 전달이다!

Left: stack. Right: heap. Stack contains two stack frames: main (with local variable bob) and changeName (local variable person). Both variables point to the same memory address on the heap for an object. Sample code shows changeName pointing its local variable to a new object. Text reads: "person here. Time to point to a new object!" Local variable bob is still pointing to the original object.

Notice that within the scope of the changeName function, person points to the object with a name property of 'Jane'. But this doesn’t affect the pointer argument—it’s still pointing to the original location in memory, and the object there is left untouched.
changeName 함수의 범위 내에서 person'Jane' 값을 가진 name 속성을 가진 객체를 가리키고 있음을 주목하세요. 하지만 이것은 포인터 인수에 영향을 미치지 않습니다—여전히 원래 메모리 위치를 가리키고 있으며, 그곳의 객체는 변경되지 않습니다.

Call Them Whatever You Want
원하는 대로 부르세요

Object references, references, pointers—these are all valid terms. The point isn’t to get into terminology wars with people online about pass by value vs. pass by reference. At the end of the day, you may all be talking about the same thing, with just a slightly different understanding.
객체 참조, 참조, 포인터—이 모든 용어가 유효합니다. 핵심은 온라인에서 값에 의한 전달과 참조에 의한 전달에 대해 용어 싸움을 벌이는 것이 아닙니다. 결국 여러분 모두는 약간 다른 이해를 가지고 있지만 같은 것을 이야기하고 있을 수 있습니다.

The reason I recommend using terms like pointer or point is because it’s a very tactile term. You can almost picture a variable pointing to something in memory. This can make it easier to visualize memory models and understand what’s going on. Plus, as we saw, it’s confusing to talk about pass by reference when there are really two distinct definitions for the term reference.
제가 포인터나 가리킨다는 용어를 추천하는 이유는 매우 직관적인 용어이기 때문입니다. 변수 하나가 메모리의 어떤 것을 가리키고 있다고 거의 시각화할 수 있습니다. 이는 메모리 모델을 시각화하고 무슨 일이 일어나고 있는지 이해하는 데 도움이 될 수 있습니다. 게다가, 우리가 본 것처럼 참조에 의한 전달에 대해 이야기하는 것은 참조라는 용어에 두 가지 뚜렷한 정의가 있기 때문에 혼란스러울 수 있습니다.

But now that you understand what references really are in JavaScript, you can continue to use this term just like everyone else does to avoid confusing others.
하지만 이제 JavaScript에서 참조가 실제로 무엇인지 이해했으니, 다른 사람을 혼란스럽게 하지 않기 위해 모두가 하는 것처럼 이 용어를 계속 사용할 수 있습니다.

Summary  요약

Let’s recap what we covered in this post:
이번 글에서 다룬 내용을 요약해 보겠습니다:

  1. What some languages call “object references” can be thought of as pointers. It helps to think of them as pointers so we don’t confuse the term reference as it’s used in “object reference” with how it’s used in “pass by reference.”
    일부 언어에서 “객체 참조(object references)”라고 부르는 것은 포인터로 생각할 수 있습니다. 이를 포인터로 생각하면 “객체 참조”에서 사용되는 참조(reference)라는 용어와 “참조에 의한 전달(pass by reference)”에서 사용되는 참조를 혼동하지 않게 됩니다.
  2. True references are aliases. The “reference” in “pass by reference” is about aliases, not “object references.”
    진정한 참조(true references)는 별칭(alias)입니다. “참조에 의한 전달”의 “참조”는 별칭에 관한 것이지 “객체 참조”에 관한 것이 아닙니다.
  3. Pointers are variables that store data indirectly, via memory addresses.
    포인터는 메모리 주소를 통해 간접적으로 데이터를 저장하는 변수입니다.
  4. In pass by value, the formal parameter receives a copy of the argument’s value.
    값에 의한 전달(pass by value)에서는 형식 매개변수가 인수의 값 복사본을 받습니다.
  5. In pass by reference, the formal parameter is an alias for the argument passed in.
    참조에 의한 전달에서는, 형식 매개변수가 전달된 인수의 별칭입니다.
  6. When we pass a pointer by value, the formal parameter receives a copy of the memory address to which the argument is pointing. This lets us modify the underlying object being pointed to.
    포인터를 값에 의한 전달로 넘기면, 형식 매개변수는 인수가 가리키는 메모리 주소의 복사본을 받습니다. 이를 통해 가리키는 기본 객체를 수정할 수 있습니다.

And that about sums it up!
이것으로 대략 정리됩니다!

Attributions  출처

Social media preview: Photo by Olesya Grichina (Unsplash).
소셜 미디어 미리보기: 사진 제공 Olesya Grichina (Unsplash).

Comments   댓글

Comment system powered by the GitHub Issues API. Post a comment on GitHub, and it'll show up below when you reload the page. (Learn more about how it works.)
댓글 시스템은 GitHub Issues API로 구동됩니다. GitHub에 댓글을 작성하면 페이지를 새로고침할 때 아래에 표시됩니다. (작동 방식에 대해 더 알아보기.)