[코드팩토리] 값 레퍼런스 복사 (Copy by value and reference)
copy by value 값에 의한 전달
copy by reference 참조에 의한 전달
기본 특징
- 기본적으로 모든 primitive 값은 copy by value다.
- 객체는 copy by reference다.
Primitive type(원시)타입과 Object type(객체)타입의 차이
- primitive ype
- 원시타입은 한 번 생성되면 해당 값을 변경할 수 없다.
- 변수의 실제 값이 저장된다.
- 값에 대한 비교가 이루어진다.
- object type
- 객체타입은 값이 생성되더라도 나중에 객체 내의 속성을 추가하거나 수정, 제거할 수 있다.
- 변수에는 객체의 Reference(참조, 주소)가 저장된다.
- 참조에 대한 비교가 이루어진다.
// 속성 수정 관련 차이점
// Primitive Type
let number = 5; // 원시타입 변수 선언
number = 10; // 이전 number의 변수의 값을 바꾸는 것이 아니라 새로운 값을 할당하는 것.
// Object Type
let person = {
firstName: 'Won Young',
lastName: 'Jang'
}; // 객체 타입 변수 선언
person.lastName = 'Kim'; // 객체의 lastName이라는 key의 value를 변경하는 것.
// 메모리 저장 관련 차이점
// Primitive Type
let a = 10;
let b = a; // a의 값이 10에 복사가 된다.
// Object Type
let obj1 = { name: 'WonYoung' };
let obj2 = obj1; // obj1의 값 자체가 복사되는 것이 아니라 obj1의 값을 저장하고 있는 메모리의 주소를 지목한다.
원시 타입은 메모리에 저장 하는 방법이 예를 들어 0x000001이라는 메모리 셀에 a = 10이라는 값을 저장했다면,
b = a 라고 선언하는 순간 b 또한 그 자체가 0x000002 라는 메모리셀에 같은 값으로 저장되게 된다.
그러나 객체 타입은 obj1는 obj1 자체가 아니라 name: 'WonYoung' 이라는 값만 0x000001이라는 메모리 셀에 저장되고, obj1은 그곳(Reference 또는 참조)을 가리키기만 한다.
그래서 obj2 = obj1이라고 선언해도 obj2 자체가 0x000001에 같이 저장되는 것이 아니라, 이것 역시 0x000001이라는 메모리셀을 지목할 뿐이다.
추가적인 예시를 들면 아래와 같다.
let wonYoung1 = '장원영';
let wonYoung2 = wonYoung1;
wonYoung2 += ' 입니다.'
console.log(wonYoung1);
console.log(wonYoung2);
console.log(wonYoung1 === wonYoung2);
wonYoung1을 원시타입으로 변수를 선언한다. 값은 '장원영'이다.
wonYoung2는 wonYoung2와 같다고 선언한다. 즉 '장원영'으로 값이 같아진다.
그런데 wonYoung2라는 변수에 ' 입니다.'라는 텍스트를 추가하도록 변경한다.
그리고 각각의 원시타입 변수를 콘솔에 출력해보니 2와 1이 같다고 선언했음에도 2만 값이 바뀌게 되었다.
이유는 Primitive Type은 변수 자체가 하나의 메모리셀에 통째로 저장되기 때문에 wonYoung2를 바꾸었다고 해서
wonYoung2의 메모리셀까지 바꾸는 것이 아니기 때문이다.
그런데 Object type은 조금 특이한 결과를 보인다.
let wonYoung1 = {
firstName: '원영',
lastName: '장'
};
let wonYoung2 = wonYoung1;
wonYoung2['lastName'] = '김';
console.log(wonYoung1);
console.log(wonYoung2);
console.log(wonYoung1 === wonYoung2);
wonYoung2라는 객체 타입의 변수에서 lastName이라는 속성의 값만 바꾸었는데, wonYoung1까지 같이 바뀌었다.
그리고 둘이 같냐는 콘솔 질문에 True로 반환하였다.
이것은 wonYoung2가 1과 같다고 선언한 순간 한 곳에 저장 되어있는 곳, 즉 '원영', '장'을 둘 다 같은 곳을 지목하고 있었는데 wonYoung2가 속성의 값을 바꿈으로써 wonYoung1까지 바뀌어버리게 된 것이다.
원시타입과 객체타입은 비교하는 대상도 다르다. 원시타입은 값을 놓고 비교하고, 객체타입은 참조를 놓고 비교한다.
// Primitive 타입
let x = 5;
let y = 5;
console.log(x === y); // true, 값이 동일하므로 true
// Object 타입
let obj3 = { name: 'Bob' };
let obj4 = { name: 'Bob' };
console.log(obj3 === obj4); // false, 각 객체는 서로 다른 메모리 주소에 저장되어 있음
원시 타입도, 객체 타입도 둘 다 각자의 변수를 선언해서 속성의 값을 할당하였는데,
원시 타입은 둘이 같냐는 질문에 값인 5가 같으니 true로 반환하였고,
객체 타입은 둘이 값이 같음에도 false를 반환하였다.
원시 타입은 x와 y도 결국 다른 메모리에 저장되어 있는데도 값이 같기 때문에 값에 의해 비교했을 때 같은 값이라고 true로 반환한 것이고,
객체 타입은 다른 메모리셀에 있기 때문에 서로 값은 같아도 가리키는 곳이 달라 reference 값이 다르기 때문에 둘이 같냐는 질문에 false를 반환한 것이다.
Spread Operator
스프레드 연산자는 앞서 배웠는데 복습하자면 ...변수명 형태로 사용하는 것이고,
array나 object의 리스트를 펼쳐서 값을 확장하거나 복사할 때 사용하는 연산자이다.
let wonYoung = {
name : '장원영',
group : '아이브'
};
let wonYoung2 = {
...wonYoung
};
console.log(wonYoung2); // wonYoung1과 같은 값이 출력됨.
console.log(wonYoung === wonYoung2); // false
즉 위의 예제에서 wonYoung의 object 값을 wonYoung2에 펼쳐서 똑같은 값으로 복사한 개념이기에,
같은 object 타입이라고 하더라도 같은 좌표를 가리키는 개념이 아니라 아예 같은 값을 새로운 메모리에 복사한 것이 된다. 따라서 이 경우엔 let wonYoung, let wonYoung2라고 변수를 두 번 선언한 것이나 다름 없기 때문에
둘 다 다른 reference를 가리키고 있어 === 는 false가 출력된다.
추가학습.Spread Operator (스프레드 연산자)
스프레드 연산자는 배열이나 객체를 확장하거나 복사할 때 사용하는 문법이다. ...변수명 으로 사용한다.
배열을 병합하는 예시는 아래와 같다.
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combinedArr = [...arr1, ...arr2];
console.log(combinedArr); // 출력: [1, 2, 3, 4, 5, 6]
새로운 요소를 추가하는 예시는 아래와 같다.
const arr = [1, 2, 3];
const newArr = [...arr, 4, 5, 6];
console.log(newArr); // 출력: [1, 2, 3, 4, 5, 6]
배열을 복사하는 예시는 위에서도 오브젝트 타입으로 다뤘지만 어레이 타입도 추가하자면 아래와 같다.
const originalArray = [1, 2, 3];
const copiedArray = [...originalArray];
console.log(copiedArray); // 출력: [1, 2, 3]
스프레드 연산자를 사용하면서 오브젝트 타입의 속성 값을 변경할 때는 순서가 중요하다.
속성을 변경하는 구문을 스프레드 연산자보다 앞에 쓰면 잘 바뀌지만,
스프레드 연산자보다 뒤에 쓰면 바뀌지 않는다.
// 스프레드 연산자를 바꾸는 구문보다 앞에 쓴 경우
let girl = {
name: '장원영',
group: '아이브'
};
let wonYoung = {
...girl,
name: '안유진'
};
console.log(wonYoung); // { name: '안유진', group: '아이브' } 로 잘 바뀌어서 출력된다.
// 스프레드 연산자를 바꾸는 구문보다 앞에 쓴 경우
let girl = {
name: '장원영',
group: '아이브'
};
let wonYoung = {
name: '안유진',
...girl
};
console.log(wonYoung); // { name: '안유진', group: '아이브' } 로 잘 바뀌어서 출력된다.
추가학습.Array 타입과 Object 타입
- Array 타입 (배열, 리스트) : let grade = [90, 80, 70, 60, 50] 과 같이 사용, 하나의 속성의 값을 나열할 때 사용한다.
- 대괄호로 감싼다.
- 위에서 90, 80과 같은 값은 요소(element)라고 부른다.
- 값에 접근할 땐 grade[0]과 같이 변수명[인덱스번호] 형태로 값을 반환받는다.
- 처음부터 인덱스번호는 0부터 시작한다.
- Object 타입 (객체) : let grade = {"장원영" : 90, "안유진" : 80} 과 같이 세부적으로 key 값을 더 넣을 수 있다.
- 중괄호로 감싼다.
- key : value가 쌍을 이룬다.
- 위에서 "장원영" : 90과 같은 값을 속성(property)라고 부른다.
- 값에 접근할 땐 grade.["장원영"]과 같이 변수명.[키값] 형태로 값을 반환받는다.
출처 : https://youtu.be/ZOVG7_41kJE?si=ujb7usAQFvJMxpp7
'Programing > JavaScript' 카테고리의 다른 글
[코드팩토리] 클래스 기본기 (Class basics) (1) | 2024.03.05 |
---|---|
[코드팩토리] try...catch (0) | 2024.03.05 |
[코드팩토리] 객체 기본 (Object basics) (0) | 2024.03.05 |
[코드팩토리] 어레이 함수 (Array Functions) (4) | 2024.03.05 |
[코드팩토리] 함수의 기본기 (Function Basics) (0) | 2024.03.04 |
댓글