모던자바스크립트 DeepDive : 11장 원시 값과 객체의 비교

te-ing·2022년 4월 22일
0
post-thumbnail

얕은 복사와 깊은 복사가 생겨나는 이유
메모리 사용의 효율성과 성능을 위해 객체는 변경 가능한 값으로 설계되어 있다. 하지만 이러한 구조적 단점에 따른 부작용이 있는데, 여러개의 식별자가 하나의 객체를 공유할 수 있다는 것이다.

원시값 ≠ 상수

원시값은 변경 불가능한 값이다. 하지만 변경불가능한 것은 변수가 아닌 값에 대한 진술이다.
변수는 메모리 공간을 식별하기 위해 붙인 이름이고 값은 변수에 저장된 데이터로써 표현식이 평가되어 생성된 결과를 말하며, 상수는 변수값을 변경할 수 없는 재할당이 금지된 변수일 뿐이다.
따라서 상수와 변경 불가능한 값은 동일하지 않다.

const o = {};
o.a=1; // 상수로 선언한 변수에 할당한 객체(값)는 변경할 수 있다.
console.log(o); // {a: 1}
  • 자바스크립트의 7개 데이터 타입 원시타입: number, string, boolean, undefined, null, symbol
    객체타입: 객체, 함수, 배열 등 ❗ 중요한 것은 자바스크립트는 객체 기반의 언어이며, 자바스크립트를 이루고 있는 거의 모든 것이 객체라는 것이다. 위 6가지 데이터 타입 이외의 값은 모두 객체 타입이다.

원시값의 불변성

var score; 라는 변수를 선언하면 score 변수가 가르키는 주소에는 undefined라는 원시값이 생성된다. 이후 score = 80; 변수에 새로운 원시 값을 재할당한다면 undefined라는 원시값을 변경하지 않고, 새로운 메모리 공간에 80을 저장한 뒤 변수가 참조하는 주소를 변경한다. 값이 이러한 특성을 불변성이라 한다.

이처럼 불변성을 갖는 원시값을 할당한 변수가 재할당 외에 변수 값을 변경할 수 없도록 하는 이유는 예기치 않게 변수값의 변경될 수 없도록 하는것이며, 이는 상태 변경이 어려워지는 것을 막기 위함이다.


문자열과 불변성

var str = 'string';
console.log(str.length); // 6
console.log(str.toUppercase()); // STRING

str[0] = 'string';
str[0] = 'S';
console.log(str); // string (에러발생X)

자바스크립트의 문자열은 원시타입이며 변경불가능하다. 따라서 var str = ‘string’ 이라는 문자열이 있을 때 str[0] = ‘S’; 일부 문자를 변경해도 반영되지 않으며 이때 에러는 발생하지 않는다.
하지만 문자열은 유사 배열 객체이면서 이터러블이므로, 배열과 유사하게 각 문자에 접근할 수 있다.


객체

객체를 변경할 때 마다 원시 값처럼 이전 값을 복사해서 새롭게 생성한다면 크기가 일정치 않으며 프로퍼티가 객체일 수도 있으므로 메모리의 효율적 소비가 어렵고 성능이 나빠진다.

따라서 메모리 사용의 효율성과 성능을 위해 객체는 변경 가능한 값으로 설계되어 있다. 하지만 이러한 구조적 단점에 따른 부작용이 있는데, 여러개의 식별자가 하나의 객체를 공유할 수 있다는 것이다.

부작용: 참조에 의한 전달(얕은 복사)

var person = {
	name: 'Lee'
};
var copy = person; // 참조값을 복사(얕은 복사)
copy.name = 'Kim';
console.log(person); // {name: "Kim" }

객체를 가리키는 변수를 다른 변수에 할당하면 참조 값이 복사되어 전달되는데, 이를 참조에 의한 전달(얕은 복사)이라 한다. 이것은 두 개의 식별자가 하나의 객체를 공유한다는 것을 의미하는데(참조 값 복사), 이때문에 원본 혹은 사본에서 객체를 변경할 때 마다 서로 영향을 주고 받게 된다.

참조값을 가지는 변수(객체), 원시값을 가지는 변수(원시값)

var person1 = {
	name: 'Lee'
};
var person2 = {
	name: 'Lee'
};
console.log(person1 === person2); // false
console.log(person1.name === person2.name); // true

객체 리터럴은 평가될 때마다 객체를 생성한다. 따라서 person1와 person2가 가리키는 객체의 내용은 같지만 다른 메모리에 저장된 별개의 객체이다. 즉 두 변수의 참조 값은 전혀 다르기 때문에 false를 반환한다.

하지만 프로퍼티 값을 참조하는 person1.name과 person2.name은 값으로 평가될 수 있는 표현식으로, 두 표현식 모두 원시 값 ‘Lee’로 평가되어 true를 반환한다.


원시값과 참조값(array, object)의 차이점을 메모리 관점에서 설명해주세요.

const로 지정한 값은 변하지 않아야 하는데 오브젝트 경우 값이 변환된다. 이는 array와 object는 참조값이고 이를 제외한 number, string 등의 값들은 원시값이기 때문이다. 변수는 메모리 공간을 식별하기 위해 붙인 이름이고, 값은 변수에 저장된 데이터의 표현식이 평가되어 생성된 결과이다.

원시값은 변경 불가능한 값으로, var score; 이후 score=80; 을 선언하면 undefined라는 원시값을 변경하지 않고 새로운 메모리 공간에 80을 저장한 뒤 변수가 참조하는 주소를 변경한다.

하지만 객체를 변경할 때 마다 원시 값처럼 이전 값을 복사해서 새롭게 생성한다면 크기가 일정치 않으며 프로퍼티가 객체일 수도 있기 때문에, 원시값처럼 성능과 메모리의 효율성이 떨어진다. 따라서 객체는 변경 가능한 값으로 설계되어 있다.

객체를 가리키는 변수를 다른 변수에 할당하면 참조 값이 복사되어 전달되는데, 여기서 두 개의 식별자가 하나의 객체를 공유하는 얕은 복사가 일어난다.

profile
병아리 프론트엔드 개발자🐣

0개의 댓글