11장 원시 값과 객체의 비교


원시값

변경 불가능한 값

원시 값은 변경 불가능한 값이다.
변수가 변경 불가능하다는 것이 아니라 값이 변경 불가능하다는 말이다.

이는 변수에 할당된 원시값을 변경할 수 없다는 것을 의미하고, 변수의 값을 변경하기 위해서는 새로운 원시값을 만들어서 할당해야한다는 것을 뜻한다.
Untitled 3.png|Untitled 3.png

문자열과 불변성

문자열은 몇 개의 문자로 이뤄졌느냐에 따라 필요한 메모리 공간의 크기가 결정됨.
JS 에서는 원시 타입인 문자열 타입을 제공.
문자열도 원시 타입으로 생성된 이후에는 변경할 수 없음.

var str = 'string';

// 문자열은 유사 배열이므로 배열과 유사하게 인덱스를 사용해 각 문자에 접근할 수 있다.
// 하지만 문자열은 원시값이므로 변경할 수 없다. 이때 에러가 발생하지 않는다.
str[0] = 'S';

console.log(str); // string

문자열은 유사 배열 객체이면서 이터러블이여서 배열과 유사하게 문자에 접근 할 수 있다.

var str = 'string';

// 문자열은 유사 배열이므로 배열과 유사하게 인덱스를 사용해 각 문자에 접근할 수 있다.
console.log(str[0]); // s

// 원시 값인 문자열이 객체처럼 동작한다.
console.log(str.length); // 6
console.log(str.toUpperCase()); // STRING

이터러블에 관한 자세한 내용은 34장 이터러블 참고

값에 의한 전달

변수에 변수를 할당할 때 무엇이 전달되는가.

JS 에서는 변수 값이 복사되어 전달된다. 즉, 두 변수의 값은 다른 메모리 공간에 저장된 별개의 값이 된다.

Untitled 1.png|300

var score = 80;

// copy 변수에는 score 변수의 값 80이 복사되어 할당된다.
var copy = score;

console.log(score, copy);    // 80  80
console.log(score === copy); // true

// score 변수와 copy 변수의 값은 다른 메모리 공간에 저장된 별개의 값이다.
// 따라서 score 변수의 값을 변경해도 copy 변수의 값에는 어떠한 영향도 주지 않는다.
score = 100;

console.log(score, copy);    // 100  80
console.log(score === copy); // false

참고로 파이썬은 변수에 변수를 할당하면 같은 메모리 공간을 참조한다고 한다.
Untitled 2 4.png|Untitled 2 4.png

엄격하게 표현하면 변수에는 값이 전달되는 게 아니라 메모리 주소가 전달됨. 식별자는 값이 아니라 메모리 주소를 기억한다.

식별자로 값을 구별해서 식별한다는 것은 식별자가 기억하고 있는 메모리 주소를 통해서 메모리 공간에 저장된 값에 접근한다는 것. 즉, 식별자는 메모리 주소에 붙인 이름이라고 할 수 있다.

JS도 어차피 재할당하면 다른 메모리 공간을 만들건데, 왜 파이썬처럼 같은 메모리를 참조하지 않고 재할당하는 걸까? 파이썬의 방법이 메모리를 절약하는 것 아닌가? #미스테리

객체

변경 가능한 값

객체는 변경 가능한 값이다.
객체를 할당한 변수에는 생성된 객체가 실제로 저장된 메모리 공간 주소가 저장되어 있고 이값을 참조값이라고 함.
객체를 생성하면 메모리 어딘가에 생성됨. 그 객체의 집주소가 변수에 할당되는 거라고 보면됨

Untitled 3 4.png|Untitled 3 4.png

그러면 왜 why? 객체는 수정가능하게 했냐

메모리를 효율적으로 사용하고, 객체 복사해서 생성하는 비용이 크기 때문에
but, 부작용으로 여러개의 식별자가 하나의 객체를 공유할 수 있음.
→ 객체를 수정하면 모든 식별자한테 영향을 끼침

얕은 복사 깊은 복사
객체에서 얕은 복사는 한 단계까지만 복사하는 것을 말하고 깊은 복사는 객체에 중첩되어 있는 객체까지 모두 복사하는 것을 말한다. ( 여기에서 복사는 참고값을 복사하는게 아닌 새로운 값 생성해서 복사하는걸 의미)

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

얕은 복사하면 중첩 객체는 그냥 참고값 복사하는 듯
원시값 변수를 다른 변수에 할당하는 것을 깊은 복사, 객체 변수를 다른 변수에 할당하는 것을 얕은 복사라고 부르기도 한다고 한다. 뭔가 좀 애매한 느낌

const v = 1;

// "깊은 복사"라고 부르기도 한다.
const c1 = v;
console.log(c1 === v); // true

const o = { x: 1 };

// "얕은 복사"라고 부르기도 한다.
const c2 = o;
console.log(c2 === o); // true

참조에 의한 전달

객체 변수를 다른 변수에 할당하면 참조 값이 복사되어 전달 된다.
Untitled 4 4.png|Untitled 4 4.png

두 개의 식별자가 하나의 객체를 공유해서 어느 한쪽에서 객체를 변경하면 서로 영향을 주고 받는다.

JS 에는 참조에 의한 전달 은 존재하지 않고 값에 의한 전달 만이 존재한다고 해서, 다른 언어에서는 참조에 의한 전달이 존재하는가? 라는 생각이 들어서 찾아봤는데, C의 포인터에 관한 설명에세도 같은 표현을 쓰더라.

굳이 따지자면 모두 값에 의한 전달이 맞지만, 전달되는 값의 종류가 원시값 인지 참조값 인지 강조하기 위한 용어라고 보면 될듯하다. 또 JS에는 포인터가 존재하지 않기 때문에 다른 언어의 값에 의한 전달 과 정확히 일치하는 것은 아니라고 한다.


var person1 = {
  name: 'Lee'
};

var person2 = {
  name: 'Lee'
};

console.log(person1 === person2); // ①
console.log(person1.name === person2.name); // ②

=== 일치 비교 연산자는 객체 비교시 참조 값을 비교하고, 원시 값 비교시 원시값을 비교한다.

  1. false
    person 1과 person2과 다른 객체를 가르키므로
  2. true
    name 은 원시값이므로 두 name은 일치한다.

reference