3. 네이티브_1

악음·2021년 7월 20일
0

i will know javascript

목록 보기
7/12
post-thumbnail

1,2 장에서 보통 네이티브(의미 그대로 원주민, 원래있던것 즉 ECMAscript 명세의 내장 객체를 의미)라고 하는 여러가지 내장 타입을 몇 차례 넌지시 얘기했다.

이번엔 그 네이티브에 대해서 알아보자
가장많이 사용하는 네이티브들은

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

자바를 아는 독자라면 자바스크립트의 String()이 문자열 값을 생성하는 String() 생성자(constructor)와 비슷하게 생겨 다음코드처럼 쓸 수 있음을 눈치챘을 것이다.

let a =new String("asdasd")
console.log(a.toString) // asdasd

네이티브는 생성자처럼 사용할 수 있지만 실제로 생성되는 결과물은 여러분의 예상과 다를 수 있다


let a =new String('abc')
typeof a // "object"   String이 아니다.
a instanceof String // true
Object.prototype.toString.call(a).toString() // "[object String]"

위 코드처럼 new String("abc") 생성자의 결과는 원시값 "abc"를 감싼 객체 래퍼이다.
또한 타입체크를 해보면 String이 아닌 object이다(하위타입에 가깝다)
객체 래퍼가 어떤식으로 생겼는지 알아보자

console.log(a)//String("abc")
//이것을 펼쳐보면
String {"abc"}
0: "a"
1: "b"
2: "c"
length: 3
__proto__: String
[[PrimitiveValue]]: "abc" //원시값

요지는 new String("abc")는 "abc"를 감싸는 문자열 래퍼를 생성하며 원시값 "abc"는 아니라는 점이다.

3.1 내부[[class]]

typeof 가 "object"인값(배열 등)에는 [[class]]라는 내부 프로퍼티(클래스 지향 개념에서의 클래스라기보단 내부 분류법(classfication)의 일부로 보자)가 추가로 붙는다. 이 프로퍼티는 직접 접근할 수 없고 Object.prototype.toString()라는 메서드에 값을 넣어 호출함으로써 존재를 엿볼 수 있다.

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

내부 [[Class]]값이 배열은 "Array", 정규식은 "RegExp"임을 알 수 있다. 대부분 내부 [[Class]]는 해당 값과 관련된 내장 네이티브 생성자를 가리키지만 그렇지 않을 때도 있다.

원시값에도 내부 [[Class]]가 있을까? 먼저 null, undefined를 보자


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

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

위와같이 Null(), undefined() 같은 네이티브 생성자는 없지만 내부 [[Class]] 같을 확인해보니 "Null", "undefined"이다.

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

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

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

3.2 래퍼박싱하기

객체 래퍼는 아주 중요한 용도로 쓰인다. 원시 값엔 프로퍼티나 메서드가 없으므로 .length, .toString()으로 접근하려면 원시 값을 객체 래퍼로 감싸줘야한다.

그렇지만 자바스크립트는 원시 값을 알아서 박싱(래핑)하므로 다음과같은 코드가 가능한것이다

let a ="abc"

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

따라서 루프조건 i<a.length 처럼 빈번하게 문자열 값의 프로퍼티/메서드를 사용해야 한다면 자바스크립트 엔진이 암시적으로 객체를 생성할 필요가 없도록 처음부터 값을 객체로 갖고있는 것이 이치에 맞는거처럼 보인다.

하지만 브라우저는 이런 흔한 경우를 스스로 최적화 하기 때문에 개발자가 직접 객체 형태로(최적화 되지 않은 방향으로) '선 최적화'(Pre-Optimize)하면 프로그램이 더 느려질 수 있다.

때문에 브라우저가 스스로 최적화 할 수있도록 new String('abc') 보단 'abc'을 사용하자

3.2.1 객체 래퍼의 함정

그래도 객체 래퍼를 사용해야 한다면 정말 조심해야 할 함정이 있다.

let a =new Boolean(false)
if(!a){
 console.log('여기가 실행되어야함!')//실행되지않는다.
  
}

문제는 객체가 truthy란 점이다. 그래서 예쌍과는 달리 안에들어있는 false값과 반대의 결과가나온다.

때문에 직접 값을 래핑하지 말자
(여기서 래핑의 원라 값을 가져오려면 a.valueOf() 를 사용해야한다.

3.3 언박싱

객체 래퍼의 원시 값은 valueOf() 메서드로 추출한다.

    let a = new String('abc')
    let b = new Number(42)
    let c = new Boolean(true)
    a.valueOf() // "abc"
    b.valueOf() // 42
    c.valueOf() // true

다음 예제에선 암시변환이 일어난다.

	let a =new String('abc')
    let b =a+"" //이때 b에는 a의 원시값 abc에 + ""가 되어 대입된다.
    b //abc
    
    typeof a // object
    typeof b // string

3.4 네이티브, 나는 생성자다

배열, 객체, 함수, 정규식 값은 리터럴 형태로 생성하는 것이 일반적이지만, 리터럴은 생서자 형식으로 만든 것과 동일한 종류의 객체를 생성한다.
래핑되지 않은 값은 없다.!

뭐 누구라도 그렇지만 간단히 레터럴 형식으로 변수에 값을 할당할것이고
굳이 생성자 까지 써가면서 코딩하진 않을 것이다

3.4.1 Array()

let a =new Array(1,2,3) //혹은 Array(1,2,3)
a //[1,2,3]

let b = [1,2,3]
b //[1,2,3]

여기서 Array()의 이상한 기능이 하나가 있는데
1개의 인자만 받고 그인자가 숫자일 경우 그 숫자만큼의 크기의 빈배열을 생성한다,,....

let a=Array(1)
a// [empty]

이부분을 잘 숙지하고 저런 코드는 쓰지말자
사실 빈배열으 만드는 것 부터가 말이 안된다.
만약 그렇게하고싶다면 빈배열을 만든뒤 length 프로퍼티에 숫자를 입력하는게 맞다..
다음과같은 코드를 확인해보자

let a =new Array(3)
a // [empty × 3]
let b =[undefined,undefined,undefined]
b// [undefined, undefined, undefined]
let c=[]
c.length=3
c// [empty × 3]

각각 브라우저마다 위에 값들이 다르게 나올 수 있음을 명심하자
그러니 빈배열따위는 만들지 않도록하자!

그러나 굳이! undefined로 채워진 배열을 만들고싶다면 다음과같은 코드를 사용하자

let a = Array.apply(null,{length:3})
a //[undefined,undefined,undefined]

3.4.2 Object(), Function(),and RegExp()

일반적으로 Object()/Function()RegExp() 생성자도 선택 사항이다(어떤 분명한 의도가 아니라면 사용하지 않는 편이 좋다)

원래 쓰던 방식대로 사용하자
함수나 오브젝트 정규식등을 생성자로 만드는 것보단 직접
{},()=>{}/function a(){},/정규식/ 으로 사용하도록하자

정규식 같은경우는 new RegExp()를 사용하여 패턴을 동적으로 정의할 경우 의미있는 유틸이라고 한다.

3.4.3 Date() and Error()

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

Date()

Date객체 값은 new Date()로 생성한다. 이 생성자는 날짜/시각 인자로 받는다(인자가 없으면 현재 날짜로 대신한다)
Date 객체는 유닉스(Unix)타임스탬프 값(1970년 1월 1일부터 흐른시간을 초단위로 환산)을 얻는 용도로 가장 많이 쓰일 것이다.
date 객체의 인스턴스로부터 getTime()을 호출하면된다

Error()

Error() 생선자는 앞에 new가 있던 없든 결과는 같다.
error객체의 주 용도는 현재의 실행 스택 콘텍스트(에러가 터진 맥락) 를 포착하여 객체에 담는 것이다.
이 실행 콘텍스트는 함수 호출 스택, error객체가 만들어진 줄 번호 등 디버깅에 도움이 될만한 정보들을 담고있다.

다음과같이 사용한다

function foo(x){
 if(!x){
  throw new Error("x를 내놔") 
 }
}

3.4.4 Symbol()

심벌은 ES6에서 처음 선보인, 새로운 원시값 타입이다, 심벌은 출돌 염려 없이 객체 ㅍ ㅡ로퍼티로 사용가능한 특별한 유일값이다.
주로 ES6의 특수한 로직에 쓰기 위해 고안되었지만 사용자도 커스텀하여 사용할 수있따.

심볼은 프로퍼티 명으로 사용 할 수있으나 프로그램 코드나 개발자 콘솔 창에서 심벌의 실제값을 보거나 접근하는 건 불가능하다.
심벌은 new를 붙이면 에러가 나니 Symbol()으로 사용하자

let mysyb=Symbol("asdasd")// Symbol(asdasd)
mysyb.toString() // "Symbol(asdasd)"
typeof mysyb // "symbol"
let b={}
b[mysyb]="foobar"
b[mysyb] //"foobar"
Object.getOwnPropertySymbols(b)// [Symbol(asdasd)]

위와같이 심벌은 전용 프로퍼티는 아니지만 (Object.getOwnPropertySymbols으로 들여다보면 공용)
본래의 사용 목적에 맞게 대부분 전용 프로퍼티로 사용한다.
지금까지 많은 개발자가 언더스코어로 이건 건들지마세요! 라고 했지만 이젠 심벌에 의해 완전히 대체될 가능성이 높다.

profile
RN/react.js개발자이며 배운것들을 제가 보기위해서 정리하기 때문에 비속어 오타가 있을수있습니다.

0개의 댓글