IT_Programming/JavaScript

자바스크립트 완벽가이드 - 3.15 값에 의한 vs. 참조에 의한

JJun ™ 2010. 7. 4. 18:32


다른 프로그램 언어와 마찬가지로 자바스크립트도 세 가지 주요한 방식으로 데이터 값을 조작할 수 있다.

 

째, 여러분은 값을 복사할 수 있다. 예를 들어, 값을 새로운 변수에 할당할 수 있다.

둘째, 여러분은 값을 함수나 메서드의 전달인자로 넘겨 줄 수 있다.

셋째, 여러분은 두 값이 동일한지를 알아보기 위해 한 값과 다른 값을 비교할 수 있다.

 

프로그램 언어를 이해하기 위해서는 이러한 세 가지 동작이 어떻게 수행되는지를 이해해야 한다.


데이터 값 조작 방식은 두 가지인데, 이 둘은 근본적으로 서로 다르다. 이 두 방식은 '값에 의한(by value)'

조작과 '참조에 의한(by reference)' 조작이라고 불린다. 값에 의한 데이터를 조작할 때 중요한 것은

데이터 값이다. 값 할당 시에는 실제로 값이 복사되며, 복사본은 변수, 객체 프로퍼티, 배열 원소에 저장되어 원본과는 완전히 별개로 저장되는 독립적인 값이다. 데이터를 값에 의해 함수에 전달하면 데이터 복사본이

전달된다. 함수 내에서 값을 변경하면 함수에 전달된 데이터 복사본만 변경된다.

원본 데이터는 영향을 받지 않는다.

 

마지막으로 값에 의해 한 데이터를 다른 데이터와 비교할 경우 두 데이터는 완전히 동일한 값

(보통 바이트별 비교 결과가 동일하다는 것을 의미)이어야 한다.


값 조작의 또 다른 방식은 '참조에 의한' 조작이다. 이 조작법에서는 값에 대한 실제 복사본이 오직 하나만

존재한다. 우리가 조작하는 것은 값에 대한 참조다.[각주:1] 값이 참조에 의해 조작될때 변수는 직접 값을

담지 않는다. 단순히 값에 대한 참조를 담을 뿐이다. 복사나 전달, 비교되는 것은 참조다.

 

따라서 참조에 의한 할당에서는 값에 대한 참조가 할당되며, 값의 복사본이나 값 자체가 할당되지는 않는다. 참조에 의해 항당된 새 변수는 원본 변수가 가리키는 값과 동일한 값을 가리킨다. 두 참조 모두 동등하게

유효하며, 둘 다 값을 조작하는 데 사용될 수 있다. 만약 참조에 의해 값이 변경되면 그 변경은 원본 참조에서도 볼 수 있다.

 

이러한 변경은 함수 밖에서도 보인다. 마지막으로 한 값과 다른 값이 참조에 의해 비교될 때는 두 참조가

동일한 값의 고유한 복사본을 가리키고 있는지를 본다. 우연히 동일한 형태가 된(예를 들어, 바이트 값이

동일하다거나) 개별적인 두 값에 대한 참조는 동일한 것 으로 취급되지 않는다.


이렇게 자바스크립트에는 두 가지 서로 다른 값 조작 방식이 존재하며, 여러분은 이 두 가지 값 조작 방식의 의미를 잘 이해해야 한다. 표 3-4에 그 의미를 정리했다. 지금까지 '값에 의한' 혹은 '참조에 의한' 값 조작

방식에 대해 대략 설명했고, 이렇게 값 조작 방식 두 가지를 구별하는 것은 모든 프로그램 언어에 적용된다.

이어지는 절에서는 두 가지 값 조작 방식의 구별이 특히 자바스크립트에서 어떻게 적용되는지 설명한다.

어떤 데이터 타입이 값에 의해 조작되고, 어떤 데이터 타입이 참조에 의해 조작되는지를 논의할 것이다.

 

 

표 3-4 값에 의한 vs. 참조에 의한

   값에 의한 참조에 의한
 복사 실제로 값이 복사된다. 두 개의 개별적이고 독립적인 복사본이 존재한다.   값에 대한 참조만 복사된다. 새로운 참조에 의해 값이 변경되면 원본 참조에서도 그 변경이 나타난다.
 전달  별개의 값 복사본이 함수에 전달된다. 복사본을 변경해도 함수 외부에 아무런 영향을 주지 않는다.  값에 대한 참조가 함수에 전달된다. 함수내에서 전달받은 참조를 통해 값을 변경하면 그 변경은 함수 외부에서도 나타난다.
 비교  동일한 값인지를 알아보기 위해 별개의 두 값이 비교된다(종종 바이트별로).  서로 동일한 값을 가리키는지 알아보기 위해 두 참조가 비교된다. 참조가 가리키는 값들이 비록 동일한 바이트들로 구성되었다 할지라도, 서로 다른 값을 가리키는 두 참조는 같지 않다.

  1. C 프로그래머나 기타 포인터 개념에 익숙한 사람들은 이곳에서 논의하는 참조와 관련된 관련된 개념을 이해할 수 있을 것이다. 그러나 자바스크립트에서는 포인터를 지원하지 않는다. [본문으로]

 

 

 

1. 기본 타입과 참조 타입

 

자바스크립트에서 기본 규칙은 다음과 같다. 기본 타입은 값에 의해 조작되고 참조 타입은 그 이름에서

알 수 있듯이 참조에 의해 조작된다. 자바스크립트에서 숫자와 불리언 타입은 기본 타입이다.

 

이들을 기본 타입으로 취급하는 이유는 정해진 개수의 작은 바이트로 구성되어 있어 자바스크립트

인터프리터가 저수준 오퍼레이션을 통해 손쉽게 조작할 수 있기 때문이다. 반면에, 객체는 참조 타입이다.

객체의 특수한 종류인 배열이나 함수도 마찬가지로 참조 타입이다. 이들 참조 타입은 임의 개수의 프로퍼티나 원소를 포함할 수 있으므로, 크기가 고정된 기본 타입을 조작하듯 쉽게 조작할 수는 없다. 객체나 배열 값은 그 크기가 상당히 커질 수 있으므로 이들을 값에 의해 조작할 수는 없다. 그 이유는 이들을 값에 의해 조작하면 복사나 비교작업에 메모리를 비효육적으로 많이 사용해야 하기 때문이다.


문자열은 어떨까? 문자열의 길이는 임의적으로 참조 타입이어야 할 것 같다. 그러나 실제로 문자열은 객체가 아니라는 간단한 이유 때문에 자바스크립트에서 보통 기본 타입으로 취급된다. 사실 문자열은 '기본 타입'

대 '참조 타입'이라는 이분법에 들어맞지 않는다. 조만간 문자열과 문자열 작동 방식을 더 설명할 것이다.


값에 의한 데이터 조작과 참조에 의한 데이터 조작의 차이점을 탐구하는 가장 좋은 방법은 예를 살펴보는 것이다. 주석 내용을 주의 깊에 읽으면서 다음 예들을 공부하라. 예 3-1은 숫자를 복사, 전달, 비교하는 예를

보여 준다. 숫자는 기본 타입이므로 이 에는 값에 의한 데이터 조작을 보여 준다.

 

 

예 3-1 값에 의한 복사, 전달, 비교


// 먼저, 값에 의한 복사를 보여 준다.
var n = 1; // 변수 n은 값 1을 담는다.
var m = n; // 값에 의한 복사. 변수 m은 별개의 값 1을 담는다.

// 값에 의한 전달을 보여 주는 용도로 사용할 함수다.
// 나중에 볼테지만 이 함수는 우리가 원하는 대로 작동하지 않는다.
function add_to_total(total, x)
{

      total = total + x; // 이 줄은 total의 내부 복사본만 변경한다.

}

// 이제 위의 함수를 호출하면서 값에 의해 변수 n과 m에 담겨 있는 숫자들을 전달한다.
// n의 값이 복사되고 복사된 값은 함수 내에서 total이라는 이름을 갖게 된다.
// 함수 내에서 m의 복사본을 n의 복사본에 더한다. 그러나 복사본에 무언가를 더하는 것은
// 함수 회부에 있는 n의 원본 값에 영향을 주지 않는다.
// 따라서, 이 함수 호출은 아무 일도 수행사지 않는다.
add_to_total(n, m);

// 이제 값에 의한 비교를 살펴보자.
// 다음 줄에서 리터럴 1은 프로그램 내에 나타난 확실히 구분되는 숫자 값이다.
// 이 리터럴을 변수 n에 담겨있는 값과 비교한다.
// 값에 의한 비교에서는 두 숫자 값이 동일한지 알아보기 위해
// 두 숫자를 구성하는 바이트들을 비교한다.
if (n == 1) m = 2; // n의 값은 리터럴 1의 값과 동일하다. 이제 m은 2가 되었다.

 


이제 예 3-2를 보자. 이 에에서는 객체를 복사, 전달, 비교한다.

객체는 참조 타입이므로 이러한 조작들은 참조에 의해 수행된다.

이 예에서 사용한 Date 객체에 대해 더 알고 싶다면 3부를 보라.

 

 

예 3-2 참조에 의한 복사, 전달, 비교


// 2007년 크리스마스를 표현하는 객체를 생성한다.
// 변수 xmas는 생성된 객체 자체를 담지 않고, 그 객체에 대한 참조를 담는다.
var xmax = new Date(2007, 11, 25);

// 참조에 의한 복사를 수행하면 원본 객체에 대한 새로운 참조를 얻는다.
var solstice = xmas; // 이제 두 변수 모두 동일한 객체 값을 참조한다.

// 여기서 객체에 대한 참조를 통해 객체를 변경한다.
solstice.setDate(21);

// 변경사항은 원본 참조에서도 보인다.
xmax.getDate(); // 원본 값인 25가 아닌 21을 반환한다.

// 객체나 배열이 함수에 전달되는 경우에도 동일하게 적용된다.
// 다음 함수는 배열의 각 원소에 어떤 값을 더한다.
// 함수에 배열의 복사본이 아닌 배열에 대한 참조가 전달되었다.
// 따라서 함수는 참조를 통해 배열의 내용을 변경할 수 있고
// 변경사항은 함수가 종료된 후에도 보인다.
function add_to_toals(totals, x)
{

      totals[0] = totals[0] + x;
      totals[1] = totals[1] + x;
      totals[2] = totals[2] + x;

}

// 마지막으로 참조에 의한 비교를 살펴보자.
// 비록 우리는 두 변수가 서로 다른 날짜를 참조하게 만들려고 했지만
// 둘 다 동일한 객체를 참조하고 있으므로 두 변수를 비교하면
// 동일하다는 결과가 나온다.
(xmax == solstice) // true로 평가된다.

// 다음에 정의되는 두 변수는 동일한 날짜를 표현하지만 서로 별개인 두 객체를 참조한다.
var xmax - new Date(2007, 11, 25);
var solstice_plus_4 = new Date(2007, 11, 25);

// '참조에 의한 비교 규칙'에 의해서, 별개의 객체는 동일하지 않다!
(xmax != solstice_plus_4) // true로 평가된다.

 

'객체나 배열을 참조에 의해 조작하기' 라는 주제에서 벗어나기 전에 명명법과 관련된 사항을

확실히 하고 넘어가겠다. '참조에 의한 전달' 이란 말은 몇 가지 의미가 있을 수 있다.

 

어떤 독자들에게는 이 말이 함수 내에서 전달인자에 새 값을 할당할 수 있거나, 함수 내에서 변경된 값이

함수 외부에 보이게 하는 함수 호출 기법을 의미할 수 있다. 이 책에서는 이 말을 그런 의미로 사용하지

않았다. 여기서는 이 말이 단순히 객체나 배열에 대한 참조가 전달되었다는 것을 의미하며, 객체 자체가

함수에 전달되었다는 것을 의미하지는 않는다. 함수 내에서는 참조를 사용하여 객체 프로퍼티나 배열 원소를

수정할 수 있다.

 

그러나 함수 내에서 전달받은 참조를 새 객체나 배열에 대한 참조로 덮어쓸 경우, 이런 변경은 함수 외부에

보이지 않는다. 이러한 의미에 익숙한 독자들은 객체나 배열이 값에 의해 전달되기는 하지만 실제로 전달되는 값은 객체 그 자체가 아니라 참조라고 말하는 것이 더 정확하다고 느낄 것이다.

 

예 3-3은 이와 관련된 예를 보여준다.

 

 

예 3-3 참조는 값에 의해 전달된다


// 이 함수는 add_to_total() 함수의 다른 버전이다. 이 함수는 제대로 작동하지 않는다.
// 그 이유는 이 함수가 배열 그 자체를 변경하기 보다는 배열에 대한 참조를
// 변경하려고 시도하기 때문이다.

function add_to_totals2(totals, x)
{

        newtotals = new Array(3);
        newtotals[0] = totals[0] + x;
        newtotals[1] = totals[1] + x;
        newtotals[2] = totals[2] + x;
        totals = newtotals;// 이 줄은 함수 외부에 아무런 영향도 주지 않는다.

}

 

 

 

 

2. 문자열 복사와 전달

 

앞에서 언급한 바와 같이 자바스크립트 문자열은 '기본 타입 대 참조 타입' 이라는 이분법에 들어맞지 않는다.

문자열은 객체가 아니므로 기본 타입으로 가정하는 것이 자연스러운 선택일 것이다.

 

만약 문자열이 기본 타입이라면 앞서 주어진 규칙에 따라 분자열은 값에 의해 조작되어야 한다.

그러나 문자열의 길이는 임의적이기 때문에 문자열을 바이트 단위로 복사하거나 전달, 비교하는 일은

매우 비효율적이다. 따라서 문자열은 참조 타입의 형태로 구현되었다고 가정하는 것이 더 적절하다.


문자열에 대한 이런 저런 가정을 세워보는 대신, 몇 가지 자바스크립트 코드를 작성하여 문자열 조작을

실험해 보자. 만약 문자열이 참조에 의해 복사되거나 전달된다면, 변수에 저장되어 있는 참조나 함수에

전달된 문자열에 대한 참조를 통해 문자열의 내용을 변경할 수 있을 것이다.


그러나 문자열 조작 실험을 위해 코드 작성을 시작하기도 전에 큰 걸림돌을 만난다.

일단 문자열의 내용을 변경할 수 있는 방법이 없다. charAt() 메서드는 문자열의 주어진 위치에 있는 문자를 반환하지만, 여기에 대응하는 setCharAt() 메서드는 존재하지 않는다.

 

여러분이 부주의해서 그런 메서드를 찾지 못한 것이 아니다. 자바스크립트에서 문자열은 의도적으로 변경하지 못하게 만들어져 있다. 즉, 문자열 내부의 문자들을 변경하는 문법이나 메서드 혹은 프로퍼티는 존재하지 않는다.


결국, 문자열은 변경할 수 없으므로 앞선 질문에 답할 수 없게 되었다. 문자열이 값에 의해 전달되는지 참조에 의해 전달되는지를 구별할 수 있는 방법은 업삳. 효율성 때문에 자바스크립트의 문자열이 참조에 의해 전달된다고 가정해도 되지만, 실제 코드 작성에 영향을 주지는 않기 때문에 어떤 가정을 내리든 별로 상관이 없다.

 

 

 

3. 문자열 비교

 

문자열이 값에 의해 전달되는지 참조에 의해 전달되는지를 구별할 수는 없지만, 문자열 관련 코드를 작성해 보면 문자열이 값에 의해 비교되는지 참조에 의해 비교되는지 알 수 있다.

 

예 3-4는 이러한 결정에 도움을 주는 코드다.

 

예 3-4 문자열이 값에 의해 비교되는가? 참조에 의해 비교되는가?


// 문자열이 값에 의해 비교되는지 참조에 의해 비교되는지를 알아내는 일은 간단하다.
// 우연히 동일한 문자들을 담게 된 명백히 다른 두 문자열을 비교한다.
// 만약 이 두 문자열이 값에 의해 비교되면 두 문자열은 동일할 것이고 참조에 의해
// 비교되면 동일하지 않을 것이다.
var s1 = "hello";
var s2 = "hell" + "o";
if (s1 == s2) document.write("Strings compared vy value");


이 예는 문자열이 값에 의해 비교됨을 보여 준다. 몇몇 프로그래머에게는 놀라운 사실일 것이다.

C나 C++, 자바에서는 문자열이 참조 타입이고 참조에 의해 비교된다. 따라서 두 문자열의 실제 내용을

비교하려면 특별한 메서드나 함수를 사용해야 한다. 그러나 자바스크립트는 고수준 언어이며,

여러분이 문자열을 비교할 때는 대부분 값에 의해 비교한다는 것을 알고 있다.

 

따라서 효율성을 위해 문자열이 (아마도) 참조에 의해 복사되거나 전달되기는 하더라도,

문자열 비교는 값에 의해 이루어진다.

 

 

 

 

4. 값에 의한 vs. 참조에 의한 : 요약

 

표 3-5는 다양한 자바스크립트 타입들이 조작되는 방식을 요약해 놓은 것이다.

 

표 3-5 자바스크립트에서 데이터 타입 조작

  타입  복사  전달  비교 
  숫자
  불리언 
  문자열 불변 불변 값 
  객체 참조 참조 참조