Front-End/JavaScript

[You Don't Know JS] #05 자바스크립트 네이티브(내장 함수)

koh1018 2022. 4. 6. 16:09
반응형

 

 

/* 이 글은 카일 심슨의 You Don't Know JS를 읽으며 배운 것들을 개인적으로 정리하고 기록하기 위한 글입니다. */

 

 

 

다음은 가장 많이 쓰는 네이티브들이다.

  • String()
  • Number()
  • Boolean()
  • Array()
  • Object()
  • Function()
  • RegExp()
  • Date()
  • Error()
  • Symbol() - ES6에서 추가됨

네이티브는 사실 내장 함수다.

 

네이티브는 생성자처럼 사용할 수 있지만 실제로 생성되는 결과물은 예상과 다르게 자신이 감싼 원시 값의 타입이 아닌 object의 하위 타입에 가깝다.

 

var a = new String("abc");

typeof a;	// "object"

예를 들어 new String("abc")은 "abc"를 감싸는 문자열 래퍼를 생성하며 원시 값 "abc"가 아니다.

(위 코드의 a를 콘솔로 찍어보면 현재 크롬 최신 버전에서는 String {0: 'a', 1: 'b', 2: 'c', lenght: 3, [[PrimitiveValue]]: "abc"}로 나온다.)

 

 

 


 

 

typeof가 'object'인 값(배열 등)에는 [[Class]]라는 내부 프로퍼티(전통적인 클래스 지향(Class-Oriented) 개념에서의 클래스라기보단 내부 분류법(Classification)의 일부라고 보자)가 추가로 붙는다.

이 프로퍼티는 직접 접근할 수 없고 Object.prototype.toString()라는 메서드에 값을 넣어 호출함으로써 존재를 엿볼 수 있다.

(참고 : Object prototypes란? -> https://developer.mozilla.org/ko/docs/Learn/JavaScript/Objects/Object_prototypes)

 

Object.prototype.toString.call([1,2,3]);  //"[object Array]"
Object.prototype.toString.call(/regex-literal/i);  //"[object RegExp]"

Object.prototype.toString.call(null);  //"[object Null]"
Object.prototype.toString.call(undefined);  //"[object Undefined]"

Object.prototype.toString.call("abc");  //"[object String]"
Object.prototype.toString.call(42);  //"[object Number]"
Object.prototype.toString.call(true);  //"[object Boolean]"

Object.prototype.toString() 메서드를 이용해 확인해보면,

먼저 첫번째와 두번째 줄을 통해 배열은 내부 [[Class]] 값이 "Array", 정규식은 "RegExp"임을 알 수 있다.

대부분 내부 [[Class]]는 해당 값과 관련된 내장 네이티브 생성자를 가리키지만 그렇지 않을 때도 있다.

 

그 아래 원시 값들의 내부 [[Class]]를 확인해보면,

null과 undefined는 Null(), Undefined()와 같은 네이티브 생성자는 없지만 내부 [[Class]] 값은 "Null", "Undefined"이다.

 

하지만 그 밖에 문자열, 숫자, 불리언 같은 단순 원시 값은 이른바 '박싱(Boxing)' 과정을 거친다.

내부 [[Class]] 값이 각각 String, Number, Boolean으로 표시된 것으로 보아 단순 원시 값은 해당 객체 래퍼로 자동 박싱 됨을 알 수 있다.

 

 

원시 값에는 프로퍼티나 메서드가 없으므로 .length, .toString()으로 접근하려면 원시 값을 객체 래퍼로 감싸줘야한다.

자바스크립트는 고맙게도 원시 값을 알아서 박싱(래핑)해준다.

따라서 다음과 같이 사용할 수 있다.

var a = "abc";

a.length;	// 3
a.toUpperCase();	// "ABC"

 

 

직접 객체 래퍼로 박싱하는 건 권하지 않는다고 한다. 엔진이 알아서 암시적으로 박싱하게 하는 것이 좋다고 한다.

 

 

 

객체 래퍼의 원시 값을 추출하는 것을 언박싱이라고 한다.

이 언박싱은 valueOf() 메서드를 사용한다.

var a = new String("abc");

a.valueOf();	// "abc"

 

자바스크립트에서는 암시적인 언박싱이 일어난다.

var a = new String("abc");
var b = a + "";	// 'b'에는 언박싱된 원시 값 "abc"이 대입된다.

typeof a;	// "object"
typeof b;	// "string"

강제 변환이 일어나 a는 여전히 객체이지만 b는 string 원시 값이 됐다.

 

 

확실히 필요해서 쓰는게 아니라면 생성자는 가급적 쓰지 않는 편이 좋다.

Array(), Object(), Function()과 같은 것들이다.

다만, 다른 생성자와는 달리 RegExp()는 정규 표현식 패턴을 동적으로 정의할 경우 의미 있는 유틸리티다.

구문이 쉽고 성능상 이점이 있다고 한다.

var name = "Kyle";
var namePattern = new RegExp("\\b(?:" + name + ")+\\b", "ig");

var matches = someText.match(namePattern);

위와 같은 코드는 자바스크립트 코드에서 수시로 등장하는데 new RegExp("패턴", "플래그") 형식으로 사용하는 것이 좋다.

 

 

네이티브 생성자 Date()와 Error()는 리터럴 형식이 없으므로 다른 네이티브에 비해 유용하다.

 

date 객체 값은 new Date()로 생성하고 날짜/시각을 인자로 받는다. (인자를 생략하면 현재 날짜/시각으로 대신한다)

date 객체는 유닉스(Unix) 타임스탬프 값(1970년 1월 1일부터 현재까지 흐른 시간을 초 단위로 환산)을 얻는 용도로 가장 많이 쓰이며 data 객체의 인스턴스로부터 getTime()을 호출하면 된다.

 

ES5부터는 Date.now()를 사용하면 현재 타임스탬프 값을 얻을 수 있다.

(new 키워드 없이 Date()를 호출하면 현재 날짜/시각에 해당하는 문자열을 반환한다.)

 

Error() 생성자는 Array()와 마찬가지로 앞에 new가 있든 없든 결과가 같다.

error 객체는 주로 함수 호출 스택, error 객체가 만들어진 줄 번호 등 디버깅에 도움이 될 만한 정보들을 담고 있는 Execution Stack Context를 포착하여 담고 있는 용도이다.

 

error 객체는 보통 throw 연산자와 함께 사용된다.

function foo(x) {
	if (!x) {
    	throw new Error("x를 받지 못했습니다!");
    }
    // ...
}

 

 

symbol은 ES6에서 처음 선보인 새로운 원시 값 타입이다.

심벌은 충돌 염려 없이 객체 프로퍼티로 사용 가능한, 특별한 '유일 값'이다. (절대적으로 유일함이 보장되지는 않는다!)

이 심벌은 사실 ES6의 특수한 내장 로직에 쓰기 위해 고안되었지만 얼마든지 정의할 수 있다.

 

심벌은 객체가 아니고 단순한 스칼라 원시 값이다.

 

심벌은 전용 프로퍼티는 아니지만 본래의 사용 목적에 맞게 대부분 전용 혹은 특별한 프로퍼티로 사용한다.

지금까지 많은 개발자가 전용/특수/내부 프로퍼티를 의미하고 싶을 때 습관적으로 써 왔던 언더스코어(_)가 앞에 붙은 프로퍼티 명도 언젠가는 심벌에 의해 완전히 대체될 가능성이 높다고 한다.

 

 

 


 

 

내장 네이티브 생성자는 각자의 .prototype 객체를 가진다. (ex: Array.prototype, String.prototype)

이 prototype 객체에는 해당 객체의 하위 타입별로 고유한 로직이 담겨 있다.

 

이를테면 문자열 원시 값을 박싱으로 확장한 것까지 포함하여 모든 String 객체는 기본적으로 String.prototype 객체에 정의된 메서드에 접근할 수 있다.

  • String#indexOf() 문자열에서 특정 문자의 위치를 검색
  • String#charAt() 문자열에서 특정 위치의 문자를 반환
  • String#substr(), String#substring(), String#Slice() 문자열의 일부를 새로운 문자열로 추출
  • String#toUpperCase(), String#toLowerCase() 대문자/소문자로 변환된 새로운 문자열 생성
  • String#trim() 앞/뒤의 공란이 제거된 새로운 문자열 생성

(참고 : 문서화 관례에 따라 String.prototype.XYZ는 String#XYZ로 줄여 쓴다. 다른 .prototype도 마찬가지다.)

 

이런 프로토타입 위임(Prototype Delegation) 덕분에 모든 문자열이 위 메서드를 같이 쓸 수 있다.

 

각 생성자 프로토타입마다 자신의 타입에 적합한 기능이 구현되어 있다.

 

 

정리

자바스크립트는 원시 값을 감싸는 객체 래퍼, 즉 네이티브를 제공한다. (String, Number, Boolean 등)

객체 래퍼에는 타입별로 쓸만한 기능이 구현되어 있어 편리하게 사용할 수 있다.

"abc" 같은 단순한 스칼라 원시 값이 있을 때, length 프로퍼티나 String.prototype에 정의된 메서드를 호출하면 자바스크립트는 자동으로 원시 값을 해당되는 객체 래퍼로 감싸는 '박싱'하여 필요한 프로퍼티와 메서드를 사용할 수 있게 도와준다.

반응형