[Modern JS Deep Dive] CH21-빌트인 객체

Boo Sung Jun·2022년 7월 15일
0

JavaScript

목록 보기
13/27
post-thumbnail

Modern JavaScript Deep Dive 스터디 - CH21 빌트인 객체

참고 자료: ⟪모던 자바스크립트 Deep Dive⟫"(이웅모 지음,위키북스, 2020)


1. 표준 빌트인 객체

자바스크립트 객체의 분류


(1) 표준 빌트인 객체

  • ECMAScript 사양에 정의된 객체
  • 애플리케이션 전역의 공통 기능을 제공
  • 자바스크립트 실행 황경과 관계없이 언제나 사용 가능
  • 전역 객체의 프로퍼티로서 제공
    -> 별도의 선언 없이 전역 변수처럼 언제나 참조 가능

    (2) 호스트 객체
  • ECMAScript 사양에 정의되어 있지 않지만 자바스크립트 실행 환경(브라우저환경이나 Node.js환경) 에서 추가로 제공하는 객체

    (3) 사용자 정의 객체
  • 사용자가 직접 정의한 객체
  • 자바스크립트는 40여개의 표준 빌트인 객체를 제공
    • Object, String, Number, Boolean, Symbol, Array, Map/Set, Function, ...
  • 프로토타입 메서드와 정적 메서드를 제공
    • 단, Math, Reflect, JSON은 정적 메서드만 제공
  • 표준 빌트인 객체의 prototype 프로퍼티에 바인딩된 객체는 다양한 기능의 빌트인 프로토타입 메서드를 제공
  • 인스턴스 생성 없이도 호출 가능한 빌트인 정적 메서드 제공
// Number 생성자 함수에 의한 Number 객체 생성
// 표준 빌트인 객체가 생성한 인스턴스의 프로토타입은 표준 빌트인 객체의 prototype 프로퍼티에 바인딩
const numObj = new Number(1.5);

// Number 프로토타입 메서드 사용
console.log(numObj.toFixed()); // 2

// Number 빌트인 객체의 정적 메서드
console.log(Number.isInteger(0.5)); // false

2. 래퍼 객체(wrapper object)

  • 원시값 에 대해 객체처럼 접근하면 생성되는 임시 객체
  • 원시값에 대해 마치 객체처럼 마침표 표기법(또는 대괄호 표기법)으로 접근
    -> 자바스크립트 엔진은 암묵적으로 연관된 객체를 생성
    -> 생성된 객체로 프로퍼티에 접근하거나 메서드를 호출
    -> 다시 원시값으로 되돌림
const str = 'hi';

// 원시 타입인 문자열이 래퍼 객체인 String 인스턴스로 변환된다.
console.log(str.length); // 2
console.log(str.toUpperCase()); // HI

// 래퍼 객체로 프로퍼티에 접근하거나 메서드를 호출한 후, 다시 원시값으로 되돌린다.
console.log(typeof str); // string
  • 래퍼 객체의 역할이 끝나면, 원시값은 다시 식별자에 연결되고 래퍼 객체는 가비지 컬렉션 의 대상이 됨
// ① 식별자 str은 문자열을 값으로 가지고 있다.
const str = 'hello';

// ② 식별자 str은 암묵적으로 생성된 래퍼 객체를 가리킨다.
// 식별자 str의 값 'hello'는 래퍼 객체의 [[StringData]] 내부 슬롯에 할당된다.
// 래퍼 객체에 name 프로퍼티가 동적 추가된다.
str.name = 'Lee';

// ③ 식별자 str은 다시 원래의 문자열, 즉 래퍼 객체의 [[StringData]] 내부 슬롯에 할당된 원시값을 갖는다.
// 이때 ②에서 생성된 래퍼 객체는 아무도 참조하지 않는 상태이므로 가비지 컬렉션의 대상이 된다.

// ④ 식별자 str은 새롭게 암묵적으로 생성된(②에서 생성된 래퍼 객체와는 다른) 래퍼 객체를 가리킨다.
// 새롭게 생성된 래퍼 객체에는 name 프로퍼티가 존재하지 않는다.
console.log(str.name); // undefined

// ⑤ 식별자 str은 다시 원래의 문자열, 즉 래퍼 객체의 [[StringData]] 내부 슬롯에 할당된 원시값을 갖는다.
// 이때 ④에서 생성된 래퍼 객체는 아무도 참조하지 않는 상태이므로 가비지 컬렉션의 대상이 된다.
console.log(typeof str, str);
  • undefined, null 원시값은 래퍼 객체를 가지지 않음
  • 래퍼 객체를 통해 원시값들에서 표준 빌트인 객체의 프로토타입 메서드, 프로퍼티를 참조 가능
    -> 굳이 new 연산자와 함께 인스턴스를 생성할 필요도 없고, 권장하지도 않음

3. 전역 객체(global object)

  • 자바스크립트 코드가 실행되기 이전(런타임 이전)에 자바스크립트 엔진에 의해 어떤 객체보다도 먼저 생성되는 특수한 객체

  • 브라우저 환경에서는 window(=== self, this, frames), Node.js 환경에서는 global 객체

    globalThis 객체

    • ES11 에서 도입
    • 브라우저 환경과 Node.js 환경 등의 전역 객체 이름을 통일시킨 것
    • ECMAScript 표준 사양을 준수하는 모든 환경에서 사용 가능
  • 표준 빌트인 객체, 호스트 객체, var 키워드로 선언한 전역 변수나 전역 함수를 프로퍼티로 가짐

  • 계층적 구조상 어떤 객체에도 속하지 않은 모든 빌트인 객체(표준 빌트인 객체, 호스트 객체)의 최상위 객체

    • 단, 프로토타입 상속 관계상에서 최상위 객체라는 의미는 아님. 어떤 객체의 프로퍼티도 아니며, 객체의 계층적 구조상 표준 빌트인 객체와 호스트 객체를 프로퍼티로 소유한다는 의미
  • 추가 특징

    • 전역 객체를 생성할 수 있는 생성자 함수가 제공되지 않음
    • 전역 객체의 프로퍼티를 참조할 때, window(또는 global)를 생략 가능
    • 표준 빌트인 객체를 프로퍼티로 가짐
    • 브라우저, Node.js 등 환경에 따라, 추가적인 프로퍼티/메서드를 가짐
    • var 키워드로 선언한 전역 변수와 선언하지 않은 변수에 값을 할당한 암묵적 전역, 전역 함수는 전역 객체의 프로퍼티가 됨
    • let, const 키워드로 선언한 전역 변수는 전역 객체의 프로퍼티가 아님
      • let, const로 선언한 변수는 보이지 않는 개념적인 블록(전역 렉시컬 환경의 선언적 환경 레코드) 내에 존재
    • 모든 자바스크립트 코드는 하나의 전역 객체를 공유

4. 빌트인 전역 프로퍼티

  • 전역 객체의 프로퍼티

1) Infinity

  • 무한대를 나타내는 숫자값

    // 전역 프로퍼티는 window를 생략하고 참조할 수 있다.
    console.log(window.Infinity === Infinity); // true
    
    // 양의 무한대
    console.log(3/0);  // Infinity
    // 음의 무한대
    console.log(-3/0); // -Infinity
    // Infinity는 숫자값이다.
    console.log(typeof Infinity); // number

2) NaN

  • Not a Number

    console.log(window.NaN); // NaN
    
    console.log(Number('xyz')); // NaN
    console.log(1 * 'string');  // NaN
    console.log(typeof NaN);    // number

3) undefined

console.log(window.undefined); // undefined

var foo;
console.log(foo); // undefined
console.log(typeof undefined); // undefined

5. 빌트인 전역 함수

  • 전역 객체의 메서드
  • 전역에서 호출할 수 있는 빌트인 함수

1) eval()

  • 사용 금지
    • 보안상 매우 취약하고, 처리 속도 느림
  • 자바스크립트 코드를 나타내는 문자열을 인수로 전달 받아, 표현식(expression)이면 문자열 코드를 런타임에 평가하여 값을 생성. 문(statement)이면 문자열 코드를 런타임에 실행
  • 자신이 호출된 위치에 해당하는 기존의 스코프를 런타임에 동적으로 수정
  • strict mode 존재시, 기존 스코프를 수정하지 않고 eval 함수 자신의 자체적인 스코프를 생성
// 표현식인 문
eval('1 + 2;'); // -> 3
// 표현식이 아닌 문
eval('var x = 5;'); // -> undefined

// eval 함수에 의해 런타임에 변수 선언문이 실행되어 x 변수가 선언되었다.
console.log(x); // 5

// 객체 리터럴은 반드시 괄호로 둘러싼다.
const o = eval('({ a: 1 })');
console.log(o); // {a: 1}

// 함수 리터럴은 반드시 괄호로 둘러싼다.
const f = eval('(function() { return 1; })');
console.log(f()); // 1

2) isFinite()

  • 전달받은 인수가 정상적인 유한수인지 검사
  • 전달받은 인수의 타입이 숫자가 아닌 경우, 숫자로 타입을 변환한 후 검사를 수행
  • 인수가 NaN으로 평가되는 값이라면 false 반환
// 인수가 유한수이면 true를 반환한다.
isFinite(0);    // -> true
isFinite(2e64); // -> true
isFinite('10'); // -> true: '10' → 10
isFinite(null); // -> true: null → 0

// 인수가 무한수 또는 NaN으로 평가되는 값이라면 false를 반환한다.
isFinite(Infinity);  // -> false
isFinite(-Infinity); // -> false

// 인수가 NaN으로 평가되는 값이라면 false를 반환한다.
isFinite(NaN);     // -> false
isFinite('Hello'); // -> false
isFinite('2005/12/12'); // -> false

3) isNaN()

  • 전달받은 인수가 NaN 인지 검사
  • 전달받은 인수의 타입이 숫자가 아닌 경우, 숫자로 타입을 변환 후 검사를 수행
// 인수가 유한수이면 true를 반환한다.
isFinite(0);    // -> true
isFinite(2e64); // -> true
isFinite('10'); // -> true: '10' → 10
isFinite(null); // -> true: null → 0

// 인수가 무한수 또는 NaN으로 평가되는 값이라면 false를 반환한다.
isFinite(Infinity);  // -> false
isFinite(-Infinity); // -> false

// 인수가 NaN으로 평가되는 값이라면 false를 반환한다.
isFinite(NaN);     // -> false
isFinite('Hello'); // -> false
isFinite('2005/12/12'); // -> false

4) parseFloat()

  • 전달받은 문자열 인수를 부동 소수점 숫자, 즉 실수로 해석하여 반환
// 문자열을 실수로 해석하여 반환한다.
parseFloat('3.14');  // -> 3.14
parseFloat('10.00'); // -> 10

// 공백으로 구분된 문자열은 첫 번째 문자열만 변환한다.
parseFloat('34 45 66'); // -> 34
parseFloat('40 years'); // -> 40

// 첫 번째 문자열을 숫자로 변환할 수 없다면 NaN을 반환한다.
parseFloat('He was 40'); // -> NaN

// 앞뒤 공백은 무시된다.
parseFloat(' 60 '); // -> 60

5) parseInt()

  • 전달받은 문자열 인수 를 정수(integer)로 해석(parsing)하여 반환

  • 인수가 문자열이 아니면, 문자열로 변환 후 정수로 해석하여 반환

  • 두 번째 인수로 진법을 나타내는 기수(radix)를 전달 가능

    • 반환값은 언제나 10진수
    // 문자열을 정수로 해석하여 반환한다.
    parseInt('10');     // -> 10
    parseInt('10.123'); // -> 10
    
    // '10'을 2진수로 해석하고 그 결과를 10진수 정수로 반환한다
    parseInt('10', 2); // -> 2
    // '10'을 8진수로 해석하고 그 결과를 10진수 정수로 반환한다
    parseInt('10', 8); // -> 8
    // '10'을 16진수로 해석하고 그 결과를 10진수 정수로 반환한다
    parseInt('10', 16); // -> 16
  • 10진수를 다른 진수로 변환하려면Number.prototype.toString(radix) 메서드 사용

    console.log(x.toString(2));  // '1111'
  • 첫 번째 문자열 내에, 숫자가 아닌 문자와 마주치면 전부 무시되고 해석된 정수값만 반환

    // 'A'는 10진수로 해석할 수 없다.
    parseInt('A0'); // -> NaN
    // '2'는 2진수로 해석할 수 없다.
    parseInt('20', 2); // -> NaN
  • '0xf'로 시작하는 문자열은 16진수로 해석

    • 2진수 리터럴(0b로 시작)은 제대로 해석하지 못함
// 16진수 리터럴 '0xf'를 16진수로 해석하고 10진수 정수로 그 결과를 반환한다.
parseInt('0xf'); // -> 15
// 위 코드와 같다.
parseInt('f', 16); // -> 15

// 2진수 리터럴(0b로 시작)은 제대로 해석하지 못한다. 0 이후가 무시된다.
parseInt('0b10'); // -> 0
// 8진수 리터럴(ES6에서 도입. 0o로 시작)은 제대로 해석하지 못한다. 0 이후가 무시된다.
parseInt('0o10'); // -> 0

6) encodeURI() / decodeURI()

  • 완전한 URI를 문자열로 전달받아 인코딩/디코딩
// 완전한 URI
const uri = 'http://example.com?name=이웅모&job=programmer&teacher';

// encodeURI 함수는 완전한 URI를 전달받아 이스케이프 처리를 위해 인코딩한다.
const enc = encodeURI(uri);
console.log(enc);
// http://example.com?name=%EC%9D%B4%EC%9B%85%EB%AA%A8&job=programmer&teacher

// decodeURI 함수는 인코딩된 완전한 URI를 전달받아 이스케이프 처리 이전으로 디코딩한다.
const dec = decodeURI(enc);
console.log(dec);
// http://example.com?name=이웅모&job=programmer&teacher

인코딩(encoding): URI(Uniform Resource Identifier)의 문자들을 이스케이프 처리하는 것
디코딩(decoding) : 인코딩된 URI를 이스케이프 이전 상태로 복원하는 것
이스케이프 처리: 는 네트워크를 통해 정보를 공유할 때, 어떤 시스템에서도 읽을 수 있는 아스키 문자 셋으로 변환하는 것

  • URL은 아스키 문자 셋으로 구성되어야 함
    -> 아스키 문자 셋에 정의되지 않은 문자와 특수 문자를 포함하지 않아야함
  • 단, 알파벳, 0~9의 숫자, -, _, ., !, ~, *, ', (, ) 문자는 이스케이프 처리에서 제외

6) encodeURIComponent() / decodeURIComponent()

  • URI 구성 요소를 인수로 전달받아 인코딩
// URI의 쿼리 스트링
const uriComp = 'name=이웅모&job=programmer&teacher';

// encodeURIComponent 함수는 인수로 전달받은 문자열을 URI의 구성요소인 쿼리 스트링의 일부로 간주한다.
// 따라서 쿼리 스트링 구분자로 사용되는 =, ?, &까지 인코딩한다.
let enc = encodeURIComponent(uriComp);
console.log(enc);
// name%3D%EC%9D%B4%EC%9B%85%EB%AA%A8%26job%3Dprogrammer%26teacher

let dec = decodeURIComponent(enc);
console.log(dec);
// 이웅모&job=programmer&teacher

// encodeURI 함수는 인수로 전달받은 문자열을 완전한 URI로 간주한다.
// 따라서 쿼리 스트링 구분자로 사용되는 =, ?, &를 인코딩하지 않는다.
enc = encodeURI(uriComp);
console.log(enc);
// name=%EC%9D%B4%EC%9B%85%EB%AA%A8&job=programmer&teacher

dec = decodeURI(enc);
console.log(dec);
// name=이웅모&job=programmer&teacher

5. 암묵적 전역

var x = 10; // 전역 변수

function foo () {
  // 선언하지 않은 식별자에 값을 할당
  y = 20; // window.y = 20;
}
foo();

// 선언하지 않은 식별자 y를 전역에서 참조할 수 있다.
console.log(x + y); // 30
  • 위의 예시에서 y는 전역 객체의 프로퍼티가 되어 마치 전역 변수처럼 동작 => 암묵적 전역
  • y는 전역 객체의 프로퍼티로 추가되었을 뿐, 변수가 아니므로 변수 호이스팅이 발생하지 않음
// 전역 변수 x는 호이스팅이 발생한다.
console.log(x); // undefined
// 전역 변수가 아니라 단지 전역 객체의 프로퍼티인 y는 호이스팅이 발생하지 않는다.
console.log(y); // ReferenceError: y is not defined

var x = 10; // 전역 변수

function foo () {
  // 선언하지 않은 식별자에 값을 할당
  y = 20; // window.y = 20;
}
foo();

// 선언하지 않은 식별자 y를 전역에서 참조할 수 있다.
console.log(x + y); // 30
  • y는 프로퍼티 이므로 delete연산자로 삭제 가능
    • 전역변수는 delete 연산자로 삭제할 수 없음
var x = 10; // 전역 변수

function foo () {
  // 선언하지 않은 식별자에 값을 할당
  y = 20; // window.y = 20;
  console.log(x + y);
}

foo(); // 30

console.log(window.x); // 10
console.log(window.y); // 20

delete x; // 전역 변수는 삭제되지 않는다.
delete y; // 프로퍼티는 삭제된다.

console.log(window.x); // 10
console.log(window.y); // undefined

0개의 댓글