Chapter 3 네이티브
네이티비는 내장된 함수이다.
가장 많이 쓰는 네이티브
- String() ➡ 네이티브는 생성자 처럼 사용 가능하지만, 실제 생성되는 원시값이 아닌 원시 값을 감싼 객체 타입이다.
- Number()
- Bollean()
- Array()
- Object()
- Function()
- Date()
- Error()
- Symbol()
3️⃣.1️⃣ 내부[[class]]
typeof 가 object인 값에는 [[class]]라는 내부 프로퍼티가 있지만(전통적인 클래스 보다는 내부 분류법의 일부),
이 프로퍼티는 Object.proptype.toString()에 메서드를 넣어 호출해야 접근 가능하다.
배열은 "Array", 정규식은 "RegExp" 내부 [[class]]값이 있다.
대부분 내부 [[class]]는 해당 관련된 내부 네이티브를 가르키지만 null과 undefined는 아니다.
Null() 과 Undefined() 같은 네이티브 생성자는 없지만 내부[[class]] 값은 "Null", "Undefined"이다.
그 밖에 문자열, 숫자, 불리언 같이 단순 원시값은 박싱 과정을 거친다.
내부[[class]]값이 String, Number, Boolean으로 표시되며, 단순 원시값은 해당 객체 래퍼로 자동 박싱된다.
3️⃣.2️⃣ 래퍼 박싱하기
객체 래퍼는 중요하다.
원시 값은 프로퍼티나 메서드가 없기에 .length나 .toString()로 접근하려면 객체 래퍼로 감싸야 한다.
Javascript는 원시값을 자동으로 박싱(래핑) 해준다.
값을 처음부터 객체로 설정하면 프로그램이 더 느려질 수 있다.
3️⃣.2️⃣.1️⃣ 💥객체 래퍼의 함정(주의점)💥
const a = new Boolean(false);
console.log(a);
if (!a) {
console.log("dd"); // 출력 안됨
}
위에서 new Boolean(false)는 false를 객체 래퍼로 감쌌지만 객체가 truthy란 점이 오류의 원인이다.
권장하지는 않지만 수동으로 원시값을 박싱하려면 Object() 함수를 이용하자(가끔 사용할 때가 있음)
const a = "abc";
const b = Object(a);
typeof a; // "string"
typeof b; // "object"
b instanceof String; // true
Object.prototype.toString.call(b); // "[objectString]"
3️⃣.3️⃣ 언박싱
객체 래퍼의 원시값은 valueOf() 메서드로 추출한다.
const a = new String("abc");
const b = new Number(42);
const c = new Bollean(true);
a.valueOf(); // "abc"
b.valueOf(); // 42
c.valueOf(); // true
다른 값을 참조할 때도 암시적으로 언박싱이 일어난다.
const a = new String("abc");
const b = a+""; // 이때 언박싱된 값 "abc"가 들어감
typeof a; // "object"
typeof b; // "string"
3️⃣.4️⃣ 네이티브, 나는 생성자
배열, 객체, 함수, 정규식 값은 리터럴 형태로 생성하는 것이 일반적 이지만,
리터럴은 생서자 형식으로 만든것과 동일한 종류의 객체를 생성한다.
하지만, 확실히 필요한 경우가 아니라면 생성자 사용은 비추이다.
3️⃣.4️⃣.1️⃣ Array()
const a = new Array(1, 2, 3);
a; // [1, 2, 3]
Array(...)와 new Array(...)는 결과적으로 같다.
💥주의할 점 : Array 생성자에서 인자로 하나의 숫자만 받으면 원소로 배열생성이 아니라
인자 만큼의 길이를 갖는 빈 배열을 생성한다.
(이럴 바엔 length 프로퍼티를 이용하자. 혼란을 피하기 위해....)
const a = Array(3);
const b = [undefined, undefined, undefined];
a.join("-"); // "--"
b.join("-"); // "--"
a.map((v, i) => i); // [undefined * 3]
b.map((v, i) => i); // [0, 1, 2]
a는 슬릇이 없기에 map 함수가 순회할 원소가 없다.
하지만 join은 슬릇이 있다는 가정하에 length 만큼 루프를 반복하기에
a.join은 결과가 나올 수 있었다.
apply()는 모든 함수에서 사용가능한 유틸리티이다.
첫번째 인자는 this 객체 바인딩이다.
두번째 인자는 인자의 배열로, 포함된 원소들이 함수의 인자로 전달된다.
const c = Array.apply(null, { length: 3 });
console.log(c); // [undefined, undefined, undefined]
Array() 함수가 호출되는 동시에 { length : 3 } 객체 값을 펼쳐 인자로 전달한다.
apply 내부에서는 0부터 3 직전까지 순회한다.
인스턴스 마다 객체에서 키를 가져온다.
💢이건 사용하지 말자..... 앞서 Array(3) 같은 코드 작성은 좋은 코드가 아니다...
3️⃣.4️⃣.2️⃣ Object(), Function(), RegExp()
위의 세가지 모두 선택사항이다. 의도가 분명히 있지 않다면 사용 안하는 것이 좋다....
1) new Object()는 사용할 일이 거의 없다. 리터럴 형태로 여러 프로퍼티를 생성 가능하기에 리터럴이 효율적이다.
const d = new Object();
d.foo = "bar";
console.log(d);
const e = { foo: "bar" }; //이 방법을 추천
console.log(e);
2) Function() 생성자는 함수의 인자나 내용을 동적으로 정의하는 드믄 경우에만 좋다.
const f = new Function("a", "return a*2");
const g = function (a) { return a * 2 };
function h(a) { return a * 2 };
const i = (a) => { a * 2 };
3) 정규 표현시은 리터럴 형식(/^a*b+/g) 형식으로 작성하는 것이 좋음.
리터럴 형식은 구문이 쉽고 성능상 이점이 있다.(JS엔진 실행전 정규 표현식을 미리 컴파일 한 후 캐시한다)
RegExp()는 정규표현식 패턴을 동적으로 정의할 때 의미가 있다.
const j = new RegExp("^a*b+", "g");
const k = /^a*b+/g; //이 방법을 추천
3️⃣.4️⃣.3️⃣ Date(), Error()
네이티브 생성자 Date() 와 Error() 는 리터럴 형식이 없으므로 다른 네이티브에 비해 유용하다.
1) date 객체 값은 new Date() 로 생성한다
date 객체는 유닉스 타임스템프 값을 얻는 용도로 가장 많이 쓰임(date객체의 인스턴스로 부터 getTime()로 호출함)
폴리필
if (!Date.now) {
Date.now = () => {
return (new Date()).getTime();
}
}
new 키워드 없이 Date() 호출 시 현재 날짜/시간에 해당하는 문자열 반환
console.log(new Date().getTime())
2) Error() 생성자는 앞에 new 키워드가 없어도 결과는 같다
error 객체의 주 용도 : 현재의 실행 콘텍스트를 포착하여 객체에 담는 것
(JS엔진 대부분 읽기 전용 프로퍼티인 .stack 으로 접근 가능)
function foo(x) {
if (!x) {
throw new Error("x 주셈");
}
}
foo(); // 에러 발생
Error 객체 인스턴스에 적어도 message 프로퍼티가 있고, type 등 다른 프로퍼티가 있을 수 있다.
그냥 error객체이 toString()을 호출하는 것이 가장 좋다.
3️⃣.4️⃣.4️⃣ Symbol()
변경 불가능한 원시값 타입이며, 충돌 염려없이 객체 프로퍼티를 사용가능한 특수 유일 값이다.
(프로퍼티 키를 만들기 위해 symbol을 사용가능)
(하위호환성은 유지하면 표준 확장, 고유 상숫값을 만드는데 사용가능)
const mysys = Symbol("my own symbol");
console.log(mysys); // Symbol(my own symbol)
console.log(mysys.toString()); // "Symbol(my own symbol)"
console.log(typeof mysys); // "symbol"
본례 프로퍼티는 아니지만 사용 목적에 맞게 전용 혹은 특별한 프로퍼티로 사용한다.
3️⃣.4️⃣.5️⃣ 네이티브 프로토타입
내장 네이티브 생성자느 각자의 .prototype 객체를 갖는다.
프로토타입 위임 덕에 모든 문자열들이 String.prototype 객체에 정의된 메서드에 접근할 수 있다.
문자열 값 변경 메서드X ➡ 수정이 일어나면 늘 기존 값으로 부터 새로운 값 생성
프로퍼티 추가 없이 네이티브 프로토타입 변경하는 것은 좋지 않다.
Array.isArray(Array.prototype); // true
Array.prototype.push(1, 2, 3);
console.log(Array.prototype); // [1, 2, 3, constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]
이상하게 작동된다...
프로토타입은 디폴트이다.
변수에 적절한 타입의 값이 할당되 있지 않다면, 디폴트 값이다.
프로토타입으로 디폴트 값을 세팅하면 메모리/CPU 낭비가 감소한다.
3️⃣.5️⃣ 정리
JS는 원시값을 감싸는 객체 래퍼, 즉 네이티브를 제공한다.
객체 래퍼에는 타입별로 편리한 기능들을 사용할 수 있다.
정의된 메서드를 호출하면 JS는 자동으로 원시값을 박싱하여 필요한 프로퍼티와 메서드를 쓸 수 있게 도와준다.
'JavaScript > You Don't know JS' 카테고리의 다른 글
I don't know JS YET) 문법 - 문과 표현식 (0) | 2022.07.05 |
---|---|
I don't know JS YET 강제변환 (0) | 2022.04.28 |
I don't know JS YET 특수값(값이 아닌 값, undefined, 특수 숫자, 특이한 동등비교, 값과 레퍼런스) (0) | 2022.03.29 |
I don't know JS YET 값( 배열, 문자열, 숫자 ) (0) | 2022.03.25 |
I don't know JS YET) 타입 (0) | 2022.03.18 |