본문 바로가기

Javascript

[JavaScript] 값(value), 레퍼런스(Reference)

반응형

값, Value


자바스크립트는 포인터라는 개념 자체가 없고, 참조하는 방법도 조금 다르다.

 

어떤 변수가 다른 변수를 참조할 수 없다. !! 포인터가 없다 !!

 

자바스크립트에서 레퍼런스는 공유된 값을 가리키므로 서로 다른 10개의 레퍼런스가 있다면,

이들은 저마다 항상 공유된 단일 값을 개별적으로 참조한다.

 

즉, 값 또는 레퍼런스의 할당 및 전달을 제어하는 구문 암시가 전혀 없다.

값의 타입만으로 값-복사, 레퍼런스-복사 둘 중 한쪽이 결정된다.

 

null, undefined, string, number, boolean, symbol 같은 단순값은 언제나 값-복사 방식으로 할당/전달된다.

객체(배열과 박싱된 객체 래퍼 전체)나 함수 등 합성 값은 할당/전달시 반드시 레퍼런스 사본을 생성한다.

CASE 1.

2 는 원시 값으로 a, b 각각 값이 할당된 것으로

a, b 는 값이 다르다.

 

var a = 2;
var b = a;
b++;

a; // 2
b; // 3

 

CASE 2.

[1, 2, 3]는 합성 값으로 공유된 개별 레퍼런스로 c, d, e, f 각각 이 값을 동등하게 참조하는 것으로

c, d 는 push(4) 로 인해 공유된 [1, 2, 3] 이 바뀌어 c, d의 값이 바뀌었다.

그러나, e, f 에서는 f 는 새로운 레퍼런스를 할당했기에 e, f 에서 참조하는 값이 다르다.

var c = [1, 2, 3];
var d = c;
d.push(4); // 실제 공유한 배열 값이 바뀌어 공유하던 c, d의 값이 바뀜

c; // [1, 2, 3, 4]
d; // [1, 2, 3, 4]

var e = [1, 2, 3];
var f = e;

e; // [1, 2, 3]
f; // [1, 2, 3]

f = [4, 5, 6];

e; // [1, 2, 3]
f; // [4, 5, 6]

 

CASE 3.

 x, a 는 모두 동일한 [1, 2, 3] 값을 가리키는 별도의 레퍼런스로
 값 자체 변경(push(4)) 후 새로운 [4, 5, 6] 값으로 할당해도 초기 레퍼런스가 a 를 참조하고 있고있던 값에는 영향이 없다.

 

function test(x) {
    x.push(4);
    x; // [1, 2, 3, 4]

    x = [4, 5, 6];
    x.push(7);
    x; // [4, 5, 6, 7]
}

var a = [1, 2, 3];

test(a);

a; // [1, 2, 3, 4]

 

CASE 4.

 x, a 는 모두 동일한 [1, 2, 3] 값을 가리키는 별도의 레퍼런스로
 값 자체 변경(push(4)) 후 해당 값을 비우고(length = 0), 새로운 [4, 5, 6, 7] 값을 넣었기에 이를 가르키는 a에 영향이 있다.

 

function test(x) {
    x.push(4);
    x; // [1, 2, 3, 4]

    x.length = 0; // 기존 배열을 즉시 비움
    x.push(4, 5, 6, 7);
    x; // [4, 5, 6, 7]
}

var a = [1, 2, 3];

test(a);

a; // [4, 5, 6, 7]

 

위의 CASE를 살펴보아 

값-복사나, 레퍼런스-복사는 내 마음대로 결정할 수 없으며 전적으로 값의 타입을 보고 엔진의 재량으로 결정된다.

 

합성 값을 값-복사에 의해 효과적으로 전달하려면 손수 값의 사본을 만들어 전달한 레퍼런스가 원본을 가르키지 않게 하면 된다.

예를 들어, 위에 예시에서 test( a.slice() ); 와 같이 인자 없이 slice()를 호출하면 전혀 새로운 배열의 사본을 만든다.

그러면 a와 x는 다른 레퍼런스를 가르키고 있게 된다.

 

마찬가지로 원시 값을 레퍼런스-복사 처럼 바뀐 값이 바로바로 반영되도록 넘기려면 윈시 값을 합성 값(객체, 배열 등)으로 감싸야한다.

 

function wrapper(object) {
    object.a = 42;
};

var obj = {
    a: 2
};

wrapper(obj);

obj.a; // 42

 

물론 객체 래퍼 Number를 사용해 프로퍼티를 추가하고, 간접적이나마 추가된 프로퍼티를 통하여 정보를 교환할 수는 있다.

그러나 이는 일반적이지 않을 뿐더러 좋은 습관도 아니다 !!

 

이러한 객체 래퍼 Number를 사용하기 보단 손수 객체 래퍼를 쓰는 편이 훨씬 나으며,

가능하다면 스칼라 원시 값을 사용하는 것이 좋다.

 

레퍼런스는 꽤 강력하지만 이따금 걸림돌이 되기도 하고, 존재하지도 않는 레퍼런스를 찾아 정처 없이 헤매기도 하니,

사용할 값 타입을 잘 정해서 간접적으로 할당/전달 로직에 반영해야 한다.

 


[타입(typeof)] chati.tistory.com/138

 

[JavaScript] 타입(typeof) : 배열, 문자열, 숫자, 값이 아닌 값

JavaScript 내장 타입 원시 타입, primitives null falsy한 유일한 원시값이지만, 타입은 'object' 인 특별한 존재 undefined 값이 없는 변수 값이거나, 선언되지 않은 변수 값 - 값이 없은 undefined : 접근 가..

chati.tistory.com

 


[참고] You Don't Know JS 타입과 문법, 스코프와 클로저 - 카일 심슨

 

 

반응형

❥ CHATI Github