Modern JavaScript Deep Dive 스터디 - CH11 원시 값과 객체의 비교
참고 자료: ⟪모던 자바스크립트 Deep Dive⟫"(이웅모 지음,위키북스, 2020)
--- | 원시 타입 | 객체 타입 |
---|---|---|
데이터 타입 | 숫자, 문자열, 불리언, null, undefined, symbol | object |
값 변경 여부 | 변경 불가 (immutable value) | 변경 가능 (mutable value) |
값 저장 방식 | 실제 값 저장 | 참조 값 저장 |
값 전달 방식 | 값이 복사되어 전달 (pass by value) | 참조 값이 복사되어 전달 (pass by reference) |
변수(variable)
vs값(value)
변수
: 하나의 값을 저장하기 위해 확보한 메모리 공간. 또는 메모리 공간을 식별하기 위해 붙인 이름값
: 변수에 저장된 데이터=> "변경 불가능하다"는 말은 변수가 아니라 값에 대한 것.
"원시 값은 변경 불가능하다."
== "원시 값 자체를 변경할 수 없다"
!= "변수 값을 변경할 수 없다"
변수(variable)
vs상수(constant)
변수
: 언제든지 "재할당"을 통해 변수 값을 변경(교체) 가능상수
: 단 한 번만 할당이 허용 -> 변경(교체) 불가
문자열 타입, 숫자 타입은 ECMAScript 사양에 의해 2바이트, 8바이트 할당.
그외 원시 타입은 브라우저에 따라 달라짐
유사 배열 객체(array-like object)
배열처럼 인덱스로 프로퍼티 값에 접근할 수 있으며, length 프로퍼티를 갖는 객체
문자열은 원시값? 객체?
문자열은 원시값
But 객체처럼 사용하면 래퍼 객체로 자동 변환 되어 사용
21.3절 참고
var str = 'string';
// 문자열은 유사 배열이므로 배열과 유사하게 인덱스를 사용해 각 문자에 접근할 수 있다.
// 하지만 문자열은 원시값이므로 변경할 수 없다. 이때 에러가 발생하지 않는다.
str[0] = 'S';
console.log(str); // string
값에 의한 전달
은 JavaScript 용어가 아니므로 오해가 있을 수 있음.
- 변수에는 값이 전달되는 것이 아니라 메모리 주소가 전달
즉, 식별자는 값이 아닌메모리 주소
를 가진다.- 식별자가 기억하고 있는 메모리 주소를 통해 메모리 공간에 저장된 값에 접근하는 방식
var score = 80;
var copy = score;
console.log(score); // 80
console.log(copy); // 80
console.log(score === copy); // true
score = 100;
console.log(score); // 100
console.log(copy); // 80
console.log(score === copy); // false
객체를 변경할 때마다 복사해서 새롭게 생성한다면 명확하고 신뢰성이 확보되지만, 객체는 크기가 매우 클 수 있고, 원시 값처럼 크기가 일정하지도 않고, 프로퍼티 값이 객체일 수도 있음
-> 복사 비용이 늘어 메모리의 효율적 소비가 어렵고 성능이 나빠짐
// 할당이 이뤄지는 시점에 객체 리터럴이 해석되고, 그 결과 객체가 생성된다.
var person = {
name: 'Lee'
};
// 프로퍼티 값 갱신
person.name = 'Kim';
// 프로퍼티 동적 생성
person.address = 'Seoul';
console.log(person); // {name: "Kim", address: "Seoul"}
var person = {
name: 'Lee'
};
// 참조값을 복사(얕은 복사). copy와 person은 동일한 참조값을 갖는다.
var copy = person;
// copy와 person은 동일한 객체를 참조한다.
console.log(copy === person); // true
// copy를 통해 객체를 변경한다.
copy.name = 'Kim';
// person을 통해 객체를 변경한다.
person.address = 'Seoul';
// copy와 person은 동일한 객체를 가리킨다.
// 따라서 어느 한쪽에서 객체를 변경하면 서로 영향을 주고 받는다.
console.log(person); // {name: "Kim", address: "Seoul"}
console.log(copy); // {name: "Kim", address: "Seoul"}
function foo(arr){
arr.push(100);
}
var myArr = new Array();
foo(myArr); // 함수 인자로 전달
// 함수에서 실행한 결과가 원본 배열에도 적용됨
console.log(myArr); // [100]
// ...
export function MyComponent() {
const [shoppingCart, setShoppingCart] = useState(['apple', 'milk', 'cake']);
// 장바구니에 물품 추가하는 함수
function addProduct(product){
// 잘못된 예시
var temp = shooppingCart;
temp.push(product);
setShoppingCart(product);
// shoppingCart는 배열의 참조 값을 가지고 있기 때문에 원소를 추가해도 값 변화 X
// -> 리렌더링 안일어남
// 올바른 예시
setShoppingCart([...shoppingCart, product]);
// shallow copy를 통해 새로운 배열을 생성 후에 변수에 재할당 -> 리렌더링 발생
}
return (
// ...
);
}
ReactDOM.render(
<MyComponent />,
document.getElementById('root');
);
//...
const o = { x: { y: 1 } };
// 얕은 복사
const c1 = { ...o }; // 35장 "스프레드 문법" 참고
console.log(c1 === o); // false
console.log(c1.x === o.x); // true
// lodash의 cloneDeep을 사용한 깊은 복사
// "npm install lodash"로 lodash를 설치한 후, Node.js 환경에서 실행
const _ = require('lodash');
// 깊은 복사
const c2 = _.cloneDeep(o);
console.log(c2 === o); // false
console.log(c2.x === o.x); // false