본문 바로가기

Javascript/모던 자바스크립트 DeepDive

[모던 자바스크립트 Deep Dive] 21 빌트인 객체

자바스크립트 객체의 분류

  • 표준 빌트인 객체 standard built-in objects / native objects / global objects
    • ECMAScript 사양에 정의된 객체 (브라우저 또는 Node.js환경에 관계 없이 사용 가능 )
    • 애플리케이션 전역 공통의 기능 제공
    • 전역 객체의 프로퍼티로 제공, 별도의 선언 없이 전역 변수처럼 언제나 참조 가능
  • 호스트 객체 host objects
    • ECMAScript 사양에 정의되지 않았지만 실행ㄹ 환경에서 추가로 제공하는 기능
    • 브라우저 환경: DOM, BOM, Canvas, XMLHttpRequest, fetch, requestAnimationFrame, SVG, Web Storage, Web Component, Web Worker  같은 클라이언트 사이드 Web API
    • Node.js: Node.js의 고유의 API 제공
  • 사용자 정의 객체  user-defined objects
    • 기본 제공되는 객체가 아닌 사용자가 직접 정의한 객체

표준 빌트인 객체

  • 자바스크립트는 Object, String, Number, Boolean, Symbol, Date, Math, RegExp, Array, Map/Set, WeakMap, WeakSet, Function, Promise, Reflect, Proxy, JSON, Error 등 40여개의 표준 빌트인 객체 제공
  • Math, Reflect, JSON을 제외한 표준 빌트인 객체는 모두 인스턴스를 생성할 수 있는 생성자 함수
  • 생성자 함수인 표준 빌트인 객체가 생성한 인스턴스의 프로토타입은 표준 빌트인 객체의 prototype  프로퍼티에 바인딩
  • 표준 빌트인 객체는 인스턴스 없이도 호출 가능한 빌트인 정적 메서드 제공 
// String 생성자 함수에 의한 String 객체 생성
const strObj = new String("Jin");
console.log(typeof strObj); // object

// String 생성자 함수를 통해 생성한 strObj 객체의 프로토타입은 String.prototype
console.log(Object.getPrototypeOf(strObj) === String.prototype); // true
// Number 생성자 함수에 의한 Number 객체 생성
const numObj = new Number(1.5);

// toFixed는 Number.prototype의 프로토타입 메서드
console.log(numObj.toFixed()); // 2;

// isInteger는 Number의 정적 메서드
console.log(Number.isInteger(3)); // true

원시 값과 래퍼 객체

  • 문자열이나, 숫자, 불리언 등의 원시값이 있는데 왜 String, Number, Boolean 같은 생성자 함수가 존재할까?
  • 문자열, 숫자, 불리언, 심벌은 암묵적으로 생성되는 래퍼 객체에 의해 마치 객체처럼 사용할 수 있음
  • 래퍼 객체 wrapper object: 문자열, 숫자, 불리언 값에 대해 객체처럼 접근하면 생성되는 임시 객체
  • 래퍼 객체의 처리가 종료되면 래퍼 객체의 [[StringData]], [[Number Data]] 등 내부 슬롯에 할당된 원시 값으로 되돌리고 래퍼 객체는 가비지 컬렉션의 대상이 됨
  • 래퍼 객체 참조용이므로 new 연산자와 함께 호출하여 문자열, 숫자, 불리언, 객체 인스턴스 생성하는 것은 권장하지 않음
  • null과 undefined는 생성자 함수가 없어 래퍼 객체가 없으므로 객체처럼 생성하면 에러 발생
const str = "hi";

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

// 래퍼 객체로 프로퍼티에 접근하거나 메서드를 호출한 후, 다시 원시 값으로 되돌림
console.log(typeof str); // string

문자열 래퍼 객체의 프로토타입 체인

전역 객체

  • 전역 객체 global object는 코드가 실행되기 이전 단계에 자바스크립트 엔진에 의해 어떤 객체보다도 먼저 생성되는 특수한 객체, 어떤 객체에도 속하지 않은 최상위 객체
  • 브라우저 환경에는 window(또는 self, this, frames) / Node.js 환경에는 global 전역 객체를 가리킴
  • 전역 객체는 개발자가 의도적으로 생성 불가능, 전역 변수를 생성할 수 있는 생성자 함수 제공 x
  • 전역 객체의 프로퍼티를 참조할 때 window(또는 global) 생략 가능
  • 전역 객체는 모든 표준 빌트인 객체를 프로퍼티로 가지고 있음
  • 자바스크립트 실행환경(브라우저 or Node.js)에 따라 추가적으로 프로퍼티와 메서드를 가짐
  • var 키워드로 선언한 전역 변수와 선언하지 않은 변수에 변수에 값을 할당한 암묵적 전역, 전역 함수는 전역 객체의 프로퍼티가 됨
  • let, const 키워드로 선언한 전역 변수는 전역 객체의 프로퍼티가 아닌 보이지 않는 개념적 블록(전역 렉시컬 환경의 선언적 환경 레코드) 내에 존재
  • 브라우저 환경의 모든 자바스크립트 코드는 하나의 전역 객체 window를 공유. 여러 개의  script 태그를 통해 자바스크립트 코드를 분리해도 하나의 전역 객체를 공유함

빌트인 전역 프로퍼티

  • 빌트인 전역 프로퍼티 built-in global property는 전역 객체의 프로퍼티 의미, 즉 애플리케이션 전역에서 사용하는 값 제공

Infinity

  • Infinity는 프로퍼티는 무한대를 나타내는 숫자값 Infinity를 갖음
// 전역 프로퍼티는 window를 생략하고 참조 가능
console.log(window.Infinity === Infinity); // true

// 양의 무한대
console.log(1 / 0); // Infinity
// 음의 무한대
console.log(-1 / 0); // -Infinity
// Infinity는 숫자 값
console.log(Infinity); // Infinity

NaN

  • NaN 프로퍼티는 숫자가 아님(Not-a-Number)을 나타내는 숫자값 NaN을 갖음
  • Number.NaN 프로퍼티와 같음
console.log(window.NaN); // NaN

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

undefined

  • unfined 프로퍼티는 원시 타입 undefined를 값으로 갖음
console.log(window.undefined); // undefined

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

빌트인 전역 함수

  • 빌트인 전역 함수 built-in global function은 애플리케이션 전역에서 호출할 수 있는 빌트인 함수로서 전역 객체의 메서드

eval

  • eval 함수는 자바스크립트 코드를 나타내는 문자열을 인수로 전달 받음
    • 전달 받은 문자열 코드가 표현식: 문자열 코드를 런타임에 평가하여 값 생성
    • 전달 받은 문자열 코드가 표현식이 아닌 문: 문자열 코드를 런타임에 실행
    • 문자열 코드가 여러 개의 문: 모든 문 실행
    • eval 함수는 기존의 스코프를 런타임에 동적으로 수정
      • strict mode에서는 기존의 스코프를 수정하지 않고 eval 함수 자신의 자체적인 스코프 생성
      • 인수로 전달 받은 문자열 코드가 let, const 키워드를 사용한 변수 선언문이면, 암묵적으로 strict mode 적용
    • eval 함수 사용은 금지해야 함
      • eval 함수를 통해 사용자로부터 입력 받은  콘텐츠 untrusted data를 실행하는 것은 보안에 취약
      • eval 함수를 통해 실행되는 코드는 자바스크립트 엔진에 의해 최적화가 수행되지 않으므로 처리가 느림
// 표현식인 문
console.log(eval("1 + 2;")); // 2
// 표현식이 아닌 문
console.log(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

// eval 함수는 자신이 호출된 위치에서 해당하는 기존의 스코프를 런타임에 동적으로 수정

const z = 1;

function foo() {
  console.log(z); // 1 - 선언 단계에 실행되었다면 undefined가 되었어야 함
  // eval 함수는 런타임에 foo 함수의 스코프를 동적으로 수정
  eval("var z = 2;");
  console.log(z); // 2
}

foo();
console.log(z); // 1

isFinite

  • 전달받은 인수가 유한수이면 true / 무한수이면 false를 반환
  • 전달 받은 인수의 타입이 숫자가 아닌 경우, 숫자로 타입을 변환 후 검사 실행 (NaN은 false 반환, null은 true 반환)

inNaN

  • 전달 받은 인수가 NaN인지 검사하여 결과를 불리언으로 반환
  • 전달 받은 인수의 타입이 숫자가 아닌 경우, 숫자로 타입을 변환 후 검사 실행

 parseFloat

  • 전달받은 문자열 인수를 부동 소수점 숫자 floating point number, 즉 실수로 해석 parsing하여 반환

parseInt

  • 전달받은 문자열 인수를 정수 integer로 해석하여 반환
  • 전달받은 인수가 문자열이 아니면 문자열로 변환한 다음 정수로 해석하여 반환
  • 두 번째 인수로 진법을 나타내는 기수 (2 ~ 36) 전달 가능, 이때 인수로 전달된 문자열을 해당 기수의 숫자로 해석하여 10진수로 해석하여 반환
    • 10진수를 해당 기수의 문자열로 변경하고 싶을 경우 Number.prototype.toString 메서드 사용
  • 첫 번째 인수로 전달된 문자열이 "0x" 또는 "0X"로 시작하는 16진수 리터럴이면 16진수로 해석하여 10진수로 반환(2진수와 8진수 리터럴은 해당 안됨) 
  • 앞 뒤 공백은 무시하고 중간에 공백이 있으면 공백으로 구분된 앞에만 해석하여 반환
// 전달 받은 문자열 인수를 문자열이 아니면 문자열로 변환하고 정수로 해석하여 반환
console.log(parseInt(10.123)); // 10

// "10"을 8진수로 해석하고, 그 결과를 10진수 정수 반환
console.log(parseInt("10", 8)); // 8
// Number.prototype.toString 메서드를 사용하여 10을 8진수로
console.log((10).toString(8)); // 12

// 16진수 리터럴 자동으로 16진수로 해석하여 10진수로 반환
console.log(parseInt("0x0000000f")); // 15

// 앞뒤 공백 무시, 중간에 공백이 있을 경우 공백으로 구분된 앞에만 해석하여 반환
console.log(parseInt(" 0x0000000f 34 ")); // 15

encodeURI / decodeURI

  • encodetURI 함수는 완전한 URI Uniform Resource Identifier를 문자열로 전달받아 이스케이프 처리를 위해 인코딩
  • URI는 인터넷에 있는 자원을 나타내는 유일한 주소를 말함. 하위 개념으로 URL, URN이 있음
  • URI 문법 형식 표준 RFC3986에 따르면 URL은 아스키 문자 셋으로만 구성되어야 하며, 쿼리 스트링 구분자로 사용되는 (=, ? , &)을 제외하고 이스케이프 처리 

URI

const uri = "http://example.com?name=진홍엽&status=jobless&hopeful";
// encodeURI 함수는 완전한 URI를 전달받아 이스케이프 처리를 위해 인코딩
const enc = encodeURI(uri);
console.log(enc); // http://example.com?name=%EC%A7%84%ED%99%8D%EC%97%BD&status=jobless&hopeful

// decodeURI 함수는 인코딩된 URI를 전달받아 이스케이프 처리 이전으로 디코딩
const dec = decodeURI(enc);
console.log(dec); // http://example.com?name=진홍엽&status=jobless&hopeful

encodeURIComponent / decodeURIComponent

  • URI 구성요소를 인수로 전달받아 인코딩
  • 쿼리 스트링 구분자로 사용되는 =, ?, &까지 인코딩하는 차이가 있음
const uriComp = "name=진홍엽&status=jobless&hopeful";

let enc = encodeURIComponent(uriComp);
console.log(enc); // name%3D%EC%A7%84%ED%99%8D%EC%97%BD%26status%3Djobless%26hopeful
let dec = decodeURIComponent(enc);
console.log(dec); // name=진홍엽&status=jobless&hopeful

enc = encodeURI(uriComp);
console.log(enc); // name=%EC%A7%84%ED%99%8D%EC%97%BD&status=jobless&hopeful
dec = decodeURI(enc);
console.log(dec); // name=진홍엽&status=jobless&hopeful

암묵적 전역

  • 암묵적 전역 implicit global: 선언하지 않은 식별자에 값을 할당하면 전역 객체의 프로퍼티를 동적 생성하여 전역 변수처럼 동작
  • 전역 객체의 프로퍼티이므로 변수가 아님. 따라서 변수호이스팅이 발생하지 않음
  • 변수가 아니라 단지 프로퍼티이므로 delete로 삭제 가능, 전역 변수는 프로퍼티이지만 delete로 삭제 불가능
// 전역 변수 x는 호이스팅 발생
console.log(x); // undefined
// 전역 객체의 프로퍼티 y는 호이스팅 발생 x
// console.log(y); // ReferenceError: y is not defined

var x = 10; // 전역 변수

function foo() {
  y = 20; // 선언하지 않은 식별자에 값을 할당 (전역 객체의 프로퍼티로 y 추가)
}

foo();

// 선언하지 않은 식별자 y를 전역에서 참조할 수 있음
console.log(x + y); // 30