새오의 개발 기록

Javascript: 원시 vs 참조값 본문

Javascript

Javascript: 원시 vs 참조값

새오: 2023. 1. 25. 14:53
 
출처: Udemy 자바스크립트 완벽 가이드

 

 

Javascript의 자료형: 원시값 vs 참조값

 


원시값 참조값
문자열, 숫자, 불리언, null, undefined와 심볼형 모든 객체
적은 메모리가 사용되고 따라서 일반적으로 스택 메모리에 저장됨
(많은 경우 힙으로 관리하기도 함)
math 객체와 같은 것으로 문자열 또는 숫자보다 더 많은 데이터를 가지고 있음. 따라서 관리하기 복잡해서 만드는 데 더 오래 걸리고 더 많은 메모리를 차지함
보통 스택으로 관리
보통 힙에서 관리
값을 복사함

변수가 포인터만을 저장하여 즉, 값 자체가 아닌 메모리 공간의 주소만 저장함

  

 

 

 

원시값

 

let name = 'Max';

let anotherName = name;

console.log(anotherName);
// Max

name = 'Manual';

console.log(antherName);
// name

 

 
  • name을 Manuel로 바꾸고 anotherUser를 살펴보면 여전히 Max가 저장되어 있음.
  • 변수 name에 저장된 값을 복사하여 anotherUser에 복사하여 저장한 것
  • 문자열과 숫자를 포함한 모든 데이터가 메모리 어딘가에 저장됨.
  • JavaScript는 항상 복사본을 만들어두고 새 변수에 저장하고 싶을 때 메모리에 있는 이 값을 이용하여 메모리 내 같은 곳에 저장된 이 값을 이용하여 해당 공간, 즉, 메모리의 주소에 저장하라고 변수에 말함.
  • 이 경우, 하나의 변수를 변경하면 다른 변수에 저장된 값에 영향을 미치는데 서로 같은 값을 가리키고 있기 때문임
  • 원시 값에서는 복사본이 만들어짐

 

 

  

 

참조값

 

let hobbies = ['Sports'];
let newHobbies = hobbies;

console.log(hobbies);
// ['Sports'];

console.log(newHobbies);
// ['Sports'];

 

  • hobbies라는 배열이 있고 현재 Sports를 가지고 있음.
  • newHobbies에 hobbies를 저장함.
  • hobbies를 출력하면 Sports가 배열로 보이고 newHobbies도 마찬가지로 같은 배열이 보임

 

let hobbies = ['Sports'];
let newHobbies = hobbies;

console.log(hobbies);
// ['Sports'];

console.log(newHobbies);
// ['Sports'];


hobbies.push('Cooking');

console.log(hobbies);
// ['Sports', 'Cooking'];

console.log(newHobbies);
// ['Sports', Cooking'];

 

 

  • hobbies에 Cooking이란 요소를 하나 더 추가하면 새로운 문자열이 hobbies에 푸시됨
  • hobbies를 출력하면 Sports와 Cooking이란 배열이 보이고 newHobbies 역시 Cooking을 포함되는데 hobbies를 newHobbies에 할당할 때 포인터만을 복사했기 때문임.
  • 따라서 요소 자체, 값 자체, 배열 자체는 복사되지 않았고 주소만을 복사한 것
  • 변수 2개 모두가 같은 메모리 공간을 가리키는 같은 포인터를 가짐
  • 객체에서도 마찬가지

 

 

 

참조값의 작동 방식 이유

  • 데이터의 불필요한 복제를 피하고 메모리가 차는 것을 방지하고 더 나은 성능을 제공하기 위함

 

 

 

 

 

 

 

배열과 객체의 복사를 다루는 방법

 

 

spread 연산자(전개 연산자)

let person = {age: 30};

let anotherPerson = person;

anotherPerson = 32;

console.log(person);
// {age: 32};


let yetAnotherPerson = { ...person }
 
 
  • yetAnotherPerson을 만들어서 이 변수에 person을 복사함
  • {}로 새로운 객체를 생성하고 전개 연산자(...)로 이 뒤에 person을 추가할 수 있음
  • 전개 연산자는 person에서 모든 키-값 쌍을 가져와 새로운 객체의 키-값 쌍에 추가함
  • 이것은 새로운 객체이기 때문에 실제로 복사하는 것입니다 메모리의 새로운 공간과 주소를 차지함
  • person은 그대로 있고 새로운 객체가 생김
  • yetAnotherPerson의 나이는 32이고 person의 나이를 30으로 바꾸어도 yetAnotherPerson의 나이는 여전히 32이며 실제로 복사가 이뤄졌기 때문임
  • 배열도 전개 연산자로 복사 가능
 
 

 

 

참조형으로 작업 시 혼동하기 쉬운 부분 1

 

 

const person1 = {age: 30};
const person2 = {age: 30};

person1 === person2;
// false

 

  • 같은 데이터를 가지고 있다고 해도 이 둘은 완전히 다른 객체임.
  • 다른 공간에 저장된 별개의, 완전히 다른 객체인 거죠 메모리의 주소가 다름
  • 배열을 비교할 때도 마찬가지

 

 

 

 

참조형으로 작업 시 혼동하기 쉬운 부분 2

 
const hobbies = ['Sports'];
hobbies.push('Cooking');

console.log(hobbies);
// ['Sports', 'Cooking']

 

  • 상수인 hobbies에 Cooking을 추가해도 오류가 발생하지 않음
  • hobbies에 저장된 것은 해당 배열 또는 객체의 주소이고 배열은 객체였고 따라서 이 상수에는 주소가 저장됨. 따라서 푸시를 호출했을 때 메모리의 데이터를 조작함.
  • 주소는 여전히 같은 주소임. 메모리의 데이터는 바뀌지만 주소는 같음.
 
const hobbies = ['Sports'];
hobbies.push('Cooking');

console.log(hobbies);
// ['Sports', 'Cooking']

hobbies = ['Sports', Running'];
// error

 

  • 만약 Sports와 Running이 있는 새로운 배열을 할당하려고 하면 에러 발생하는데 '=' 기호를 사용했기 때문.
  • 배열은 실제로 저장된 것이 아니고 여기에 새로운 배열을 만들었고 메모리에서 새로운 공간을 차지하는, 새로운 주소를 갖는다는 뜻임.
  • push를 이용해 저장된 값을 바꾸는 것은 허용되며 = 기호를 사용하여 새로운 값을 할당하는 것은 허용되지 않음.

 

 


 

 

const person = {age: 30};

person.age = 32;

console.log(age); 
// 32

 

  • person이 상수로 저장되어 있고 age를 30으로 설정함.
  • age에 접근하여 32로 바꿀 수 있음(에러 발생 X)
  • 여기에서 연산자 할당은 age에 사용됨. person이 아님.
  • person에 새로운 객체를 설정했다면 에러 발생했을 것.
  • 따라서 메모리에 있는 데이터는 확실히 바뀌지만 주소는 바뀌지 않음.

 

 

 

 

 

 

 

 

 

'Javascript' 카테고리의 다른 글

Javascript: 가비지 컬렉터  (0) 2023.01.25
Javascript: 코드 실행의 원리(힙, 스택)  (1) 2023.01.25
Javascript: 코드의 분석과 컴파일링  (0) 2023.01.25
Javascript: ES6와 ES6의 차이  (0) 2023.01.25
Javascript: Math.floor()  (0) 2023.01.19