오늘은 프리미티브와 객체 간의 관계가 특히 빌트인 타입을 다룰 때 왜 복잡한 지 알아보겠습니다.
이전 문자열 튜토리얼과 객체 및 타입 튜토리얼에서 약간 이상한 점을 느꼈을 겁니다.
프리미티브는 매우 단순하다고 여러 번 언급했습니다. 객체(Objects)와는 달리 조작할 수 있는 속성이 없습니다.
그러나 아래 코드에서 보듯이 프리미티브는 약간의 이상함을 가지고 있는거 같습니다.
let greeting = "Hello, everyone!";
let shouting = greeting.toUpperCase(); //toUpperCase 함수는 무엇이고 어디서 왔나요?
이 간단한 코드에서 알 수 있듯이 텍스트 형식으로 기본 값을 저장하는 greeting 변수는 toUpperCase 메서드에 액세스 할 수 있는 것 같습니다.
어떻게 가능합니까?
어떤 방법으로 가능한 건가요?
이 물음에 대한 대답이 이번 강좌의 주된 내용이 될 겁니다.
문자열만 이상하지 않다.
문자열이 처음 보기에 가장 이상하다고 생각되어 우리의 프리미티브와 객체 간의 혼란의 주요 범인이 문자열이라고 생각하는 것은 당연합니다.
그러나 아래에서 보듯이 많은 빌트인 타입도 비슷한 이상함(?)을 가지고 있습니다.
타입 |
하는 일 |
Array |
데이터를 모아서 저장, 검색, 조작하도록 지원 |
Boolean |
boolean 프리미티브 주위의 래퍼 역할을합니다. 대부분 true or false 관련입니다. |
Date |
날짜를보다 쉽게 표현하고 작업 할 수 있습니다. |
Function |
특정 일을 함께하는 코드를 호출 할 수 있습니다. |
Math |
숫자 작업을 더 잘할 수 있도록 도와주는 괴상한 그룹 |
Number |
number 프리미티브를 감싸는 래퍼 역할을합니다. |
RegExp |
텍스트의 패턴 일치를 위한 많은 기능을 제공합니다. |
String |
string 프리미티브를 감싸는 래퍼 역할을합니다. |
부울, 숫자 또는 문자열 프리미티브로 작업할 때마다 해당 프리미티브와 동등한 객체의 속성에 액세스 할 수 있습니다.
다음 섹션에서 정확히 무슨 일이 일어나고 있는지 볼 수 있습니다.
문자열 더 살펴보기
자라면서 부모님한테 배운 것처럼 우리는 일반적으로 리터럴 폼으로 문자열을 사용합니다.
let primitiveText = "Homer Simpson";
앞서 표에서 보았 듯이 문자열은 객체로도 사용할 수 있습니다.
새 객체를 만드는 방법은 여러 가지가 있지만 문자열과 같은 내장(built-in) 타입에 대한 객체를 만드는 가장 일반적인 방법은 new 키워드 다음에 String이라는 단어를 사용하는 것입니다.
let name = new String("Homer Simpson");
이 경우 String은 일반적인 단어가 아닙니다.
객체를 만드는 데 사용되는 생성자 함수로 알려져 있습니다.
객체를 생성하는 여러 가지 방법이 있는 것처럼 String 객체를 생성하는 방법도 여러 가지가 있습니다.
어쨌든, 문자열의 프리미티브(primitive) 형태와 객체(object) 형태의 주요 차이점은 객체 형태가 가지고 다니는 추가 수화물의 양입니다.
시각화를 통해 다시 보기로 합시다. primitiveText 변수와 해당 수하물은 다음과 같습니다.
프리미티브(primitive) 문자열의 경우 위 그림처럼 해당 수화물이 많지 않습니다.
하지만 name이라는 String 객체를 시각화해야 한다면 다음과 같습니다. 아마 놀라실 겁니다.
Homer Simpson이라는 텍스트에 대한 포인터를 포함하는 name 변수가 있습니다.
또한 String 객체와 함께 제공되는 다양한 속성과 메서드를 모두 가지고 있습니다.
indexOf, toUpperCase 등도 사용할 수 있습니다.
Objects를 자세히 살펴보면 이 다이어그램이 정확히 무엇을 나타내는지 전반적인 개요를 얻을 수 있으므로 여기에 표시되는 내용에 대해 너무 걱정하지 마십시오.
모든 프리미티브의 객체 형태는 많은 기능을 수반합니다.
왜 중요할까요?
처음부터 다시 생각해 봅시다. 우리의 문자열은 프리미티브(primitive)입니다.
프리미티브(primitive) 타입이 어떻게 객체의 속성(properties)을 사용할 수 있는 거죠?
그 대답은 JavaScript가 정말 이상하다는 것입니다.
다음 문자열이 있다고 가정해 보겠습니다.
let game = "Dragon Age: Origins";
game 변수는 리터럴 텍스트로 할당된 매우 명확하게 문자열 프리미티브(primitive)입니다.
이 텍스트의 길이에 액세스 하려면 다음과 같이해야 합니다.
let game = "Dragon Age: Origins";
console.log(game.length);
자바스크립트가 game.length 문구를 해석하게 되면, 자바스크립트는 기본적인 프리미티브 문자열을 그에 해당하는 문자열 객체로 변환합니다.
그러나 여기서 명심해야 할 것은 game.length라는 game 문자열 변수의 길이를 알아낸 후에는
즉, 목적을 달성한 후에는 잠시 객체로 변환된 그 객체는 사라지고 우리가 원래 목적했던 game 변수의 길이라는 숫자만 리턴합니다.
그리고 여전히 game 변수는 문자열 프리미티브(primitive)로 남아 있습니다.
이 변환은 프리미티브(primitive)에 대해서만 발생합니다. 명시적으로 String 객체를 생성한다면 그 객체는 영구적으로 유지됩니다.
let gameObject = new String("Dragon Age:Origins");
이 경우 gameObject 변수는 타입이 객체(Object)인 것이 명확합니다.
이 변수는 문자열을 수정하거나 참조를 변경하지 않는다면 계속해서 객체(Object) 타입을 유지할 겁니다.
프리미티브(primitive)가 객체로 모핑 한 다음 다시 프리미티브(primitive)로 모핑 하는 것은 모든 프리미티브(primitive) 요소에 고유한 것입니다.
데이터 타입을 체크하면 위에서 말한 모든 것을 쉽게 확인할 수 있습니다.
데이터 타입 체크는 typeof 키워드를 사용하면 됩니다.
let game = "Dragon Age: Origins";
console.log("Length is: " + game.length);
let gameObject = new String("Dragon Age:Origins");
console.log(typeof game); // string
console.log(typeof game.length); // number
console.log(typeof gameObject); // object
좀 헷갈리지만 이해되셨죠?
결론
이 시점에서, 왜 자바스크립트라는 언어는 이렇게 이상하게 만들어졌을까요?
좀 더 중용한 작업을 할 때 프리미티브가 객체로 변한다면, 왜 항상 객체로 사용하 않을까요?
정답은 메모리 소비와 관련이 있습니다.
객체 형태가 프리미티브와 비교할 때 얼마나 많은 짐을 운반하는지에 위 다이어그램에서 보았듯이, 추가 기능에 대한 모든 포인터는 리소스를 소모합니다.
JavaScript의 설루션은 타협입니다. 텍스트, 숫자, 부울과 같은 모든 리터럴 값은 선언되고 그대로 사용되는 경우 프로미티브 타입으로 유지됩니다.
필요한 경우에만 해당 객체 형식으로 변환됩니다.
자바스크립트는 해당 애플리케이션이 메모리를 적게 쓰도록 하기 위해 이렇게 변환된 객체는 목적을 달성하면 신속하게 폐기됩니다.( 가비지 수집 - garbage collected)
다음 편에서는 자바스크립트 숫자(Numbers)에 대해 알아보겠습니다.
'코딩 > Javascript' 카테고리의 다른 글
[JS-중급편-OOP] 7.Getters and Setters (0) | 2020.12.26 |
---|---|
[JS-중급편-OOP] 6.숫자(Numbers) (0) | 2020.12.24 |
[JS-중급편-OOP] 4.문자열과 변수 결합 (0) | 2020.12.23 |
[JS-중급편-OOP] 3.문자열(String) (0) | 2020.12.22 |
[JS-중급편-OOP] 2.배열(Array) (0) | 2020.12.20 |