원시 타입은 아래와 같습니다.
Boolean
null
undefined
Number
String
Symbol (New in ECMAScript 6)
💡이외 모든 값은 객체(Object) 타입이며, 객체 타입은 변경이 가능한 값(mutable value)이다.
String 으로 예로 들어보자!
C 언어와는 다르게 Javascript의 문자열은 변경 불가능한 값(immutable value) 이다. (변경이 불가능하다는 뜻은 메모리 영역에서의 변경이 불가능하다는 뜻이다. 재할당은 가능하다)
아래 예시들을 보면 쉽게 이해할 수 있다.
var str = 'Hello';
str = 'world';
첫번째 줄이 실행되면 메모리에 문자열 'Hello'가 생성되고, 변수 str은 'Hello'의 메모리 주소를 가리킨다. 그리고 두번째 줄이 실행되면 이전에 생성되었던 문자열 'Hello'를 수정하는 것이 아니라 새로운 문자열 ‘world’를 메모리에 생성하고 식별자 str은 이것을 가리킨다. 이때 문자열 ‘Hello’와 ‘world’는 모두 메모리에 존재하고 있다. 변수 str은 문자열 ‘Hello’를 가리키고 있다가 문자열 ‘world’를 가리키도록 변경되었을 뿐이다. *(원시 타입은 불변한 값이기 때문)
var statement = 'I am an immutable value'; // string은 immutable value
var otherStr = statement.slice(8, 17);
console.log(otherStr); // 'immutable'
console.log(statement); // 'I am an immutable value'
2행에서 Stirng 객체의 slice() 메소드는 statement 변수에 저장된 문자열을 변경하는 것이 아니라 사실은 새로운 문자열을 생성하여 반환하고 있다. 문자열은 변경할 수 없는 immutable value(변경 불가능한 값)이기 때문이다.
var arr = [];
console.log(arr.length); // 0
var v2 = arr.push(2); // arr.push()는 메소드 실행 후 arr의 length를 반환
console.log(arr.length); // 1
위 예시를 보면 arr는 배열의 요소가 없다가, 생겼다.
결과의 복사본을 리턴하는 문자열의 메소드 slice()와는 달리 배열(객체)의 메소드 push()는 배열 원본 자체를 변경한다.
배열은 객체이고, 객체는 불변한 값이 아닌 변경 가능한 값
이기 때문이다.
💡 결과적으로, 원시 값의 메소드는 복사본을 리턴하고, 배열(객체)의 메소드는 기존 배열 자체를 변경한다!
var user1 = {
name: 'Lee',
address: {
city: 'Seoul'
}
};
var user2 = user1; // 변수 user2는 객체 타입이다.
user2.name = 'Kim';
console.log(user1.name); // Kim
console.log(user2.name); // Kim
위 코드를 보면 user2.name 값을 변경하니 user1의 값도 똑같이 변경된 것을 알 수 있다. user1 와 user2는 값이 변할 수 있는 객체이며 동일한 값을 가리키는 메모리 주소(변수)이기 때문이다.
만약 의도적으로 하나의 값에 2개의 변수를 지정한 것이 아니라면 이 사실을 인지하고 적절히 대응해야 한다.
🙈 위와 같은 일을 방지하려면,
Object.freeze()를 사용하면 마치 원시 타입처럼 값이 변하지 않는 불변(immutable) 객체로 만들수 있다.
const user1 = {
name: 'Lee',
score:[1,2]
};
// Object.assign은 완전한 deep copy를 지원하지 않는다.
const user2 = Object.assign({}, user1, {name: 'Kim'});
console.log(user1.name); // Lee
console.log(user2.name); // Kim
user1.score.push(3);//[1,2,3]
Object.freeze(user1); // freeze()를 사용하면,
Object.freeze(user1.score); // freeze()를 사용하면,
user1.name = 'Kim'; // 이 줄이 실행되지 않는다.
user1.score.push(3);// 이 줄이 실행되지 않는다.
console.log(user1); // { name: 'Lee', score:[1,2] }
console.log(Object.isFrozen(user1)); // true
🤓 결론
자바스크립트의 데이터타입 중 원시 타입은 값이 변하지 않는 불변 값이고, 객체는 값이 바뀔 수 있는 변경 가능한 값이다.