자바스크립트의 7가지 데이터 타입 (숫자, 문자열, boolean, null, undefined, 심벌, 객체 타입)은 크게 원시 타입(primitive type)과 객체 타입(object/reference type)으로 구분된다.

원시 타입과 객체 타입은 크게 세 가지 측면에서 다르다.

  • 원시 타입의 값은 변경 불가능한 값(immutable value)이다.
    객체는 변경 가능한 값(mutable value)이다.
  • 원시 값을 변수에 할당하면 변수(확보된 메모리 공간)에는 실제 값이 저장된다.
    객체를 변수에 할당하면 변수(확보된 메모리 공간)에는 참조 값이 저장된다.
  • 원시 값을 갖는 변수를 다른 변수에 할당하면 값이 복사되어 전달된다. (pass by value)
    객체를 가리키는 변수를 다른 변수에 할당하면 원본의 참조 값이 복사되어 전달된다. (pass by reference)

더 상세하게 알아보도록 하자.

원시 값 (primitive type value)


원시 타입의 값, 즉 원시 값은 변경 불가능한 값이다. 즉, 한번 생성된 원시 값은 읽기 전용(read only) 값으로서 변경할 수 없다.

변수는 하나의 값을 저장하기 위해 확보한 메모리 공간 자체의 이름이고 값은 변수에 저장된 데이터로서 표현식이 평가되어 생성된 결과를 말한다.

변수는 언제든지 재할당을 통해 변수 값을 변경(가리키고 있는 공간값을 교체) 할 수 있다.

상대 개념인 상수는 재할당이 금지된 변수일 뿐이다.

즉, 상수와 변경 불가능한 값은 다른 개념이고 동일시하면 안된다.

score변수가 선언되고 할당된 후 값이 변경되는 과정에 대한 그림이다.

원시 값인 number는 immutable한 값이므로 80을 90으로 변경하는 것이 아니라 90이라는 숫자로 새로운 메모리 공간을 할당한 후 변수가 가리키는 공간 값을 바꿔주는 형태이다.

불변성을 갖는 원시 값을 할당한 변수는 재할당 외에 변수 값을 변경할 수 있는 방법이 없다.

문자열과 불변성


문자열도 마찬가지로 원시 값이므로 불변성을 띄고 위와 같은 절차를 따른다.

ECMAScript 사양에 문자열 타입(2 byte) 숫자 타입(8 byte) 외의 원시 값은 크기를 명확히 규정하고 있지는 않아서 브라우저 제조사의 구현에 따라 타입 크기가 다를 수 있다.

문자열의 독특한 성질에 대해서 짚고 넘어가야 할 것 같다.

문자열은 원시 값이지만 유사 배열 객체이면서 iterable이므로 배열처럼 각 문자에 접근이 가능하다.

유사 배열 객체?

배열처럼 index로 프로퍼티 값에 접근할 수 있고 length 프로퍼티를 갖는 객체를 말함.

cosnt str = 'string'

str[0] = 'S';

str.length; // 6
str; // string

하지만, 코드와 같이 이미 생성된 문자열의 일부 문자를 변경해도 반영되지 않는다.

문자열은 어쨌든원시 값이기 때문이다. 이는 데이터의 신뢰성을 보장한다.

문자열의 값을 변경하려면 기존 문자열의 변경이 아닌 새로운 할당이 일어나야 할 것이다.

pass by value


let score = 80;
let copy = score;

console.log(score, copy) // 80 80

score = 100;

console.log(score, copy) // 100 80

이 코드의 핵심은 “변수에 변수를 할당했을 때 무엇이 어떻게 전달되는가?”이다.

copy변수에도 80이 할당이 되는데 이 때, 새로운 숫자 값 80이 생성되어 copy 변수에 할당된다.

이를 값에 의한 전달이라 한다.

ECMAScript 사양에는 변수를 통해 메모리를 어떻게 관리해야 하는 지 정의 되어 있지 않기 때문에 실제 자바스크립트 엔진을 구현하는 제조사에 따라 실제 내부 동작 방식은 차이가 있을 수 있다.

위 그림은 할당 과정에서 원시 값이 복사되는 것으로 표현 됬지만

아래 그림처럼 한쪽 변수에 재할당이 이뤄졌을 때 비로소 새로운 메모리 공간에 저장하도록 동작할 수도 있다.

또한 값에 의한 전달이라는 용어도 ECMAScript 사양에는 등장하지 않는다.

객체 지향언어의 특성상 pass by value 와 reference라는 용어를 사용하지만 공유에 의한 전달(pass by sharing)이라고 표현하는 경우도 있다.

JS에서는 엄격하게 표현하면 변수에는 값이 전달되는것이 아니라 메모리 주소가 전달되기 때문이다. 변수와 같은 식별자는 값이 아니라 메모리 주소를 기억하고 있기 때문이다.

하지만 공통적인 결론은 두 변수의 원시 값은 서로 다른 메모리 공간에 저장된 별개의 값으로 재할당을 통해 값을 변경하더라도 서로 간섭할 수 없다는 것이다.

pass by reference


참조에 의한 전달은 여러 식별자가 하나의 객체를 공유할 수 있다는 것을 의미한다.

이로 인해 여러 가지 부작용이 발생할 수 있다.

const person = {
	name: 'Lee'
};

const copy = person;

객체를 가리키는 변수를 다른 변수에 할당하면 참조 값이 복사되어 전달된다.

copy와 person 두 개의 변수는 하나의 객체를 가리키고 있고 참조 값에 대한 주소만 다른 곳에 저장을 해 두었을 뿐이다.

따라서 copy나 person 중 어느 하나라도 변경이 일어나게 되면 서로 영향을 주고 받을 수 밖에 없게 된다.

얕은 복사와 깊은 복사


객체를 프로퍼티 값으로 갖는 경우 얕은 복사는 한 단계까지 복사하는 것을 말하고 (객체 통으로 복사)
깊은 복사는 객체에 중첩되어 있는 객체까지 모두 복사하는 것을 말한다.

const o = { x: { y: 1 } };

/* 걍 복사?? 때로는 얕은 복사라고 부르기도 함... */
const c3 = o;
c3 === o // true
c1.x === o.x // true

/* 얕은 복사 */
const c1 = { ...o};
c1 === o // false
c1.x === o.x // true

/* 깊은 복사 */
const _ = require('lodash');
const c2 = _.cloneDeep(o);
c2 === o // false
c2.x === o.x // false
profile
프론트엔드 신입 육아과정🍼

0개의 댓글