빌트인 객체와 this
- 7.1 자바스크립트 객체의 분류
- 7.2 표준 빌트인 객체
- 7.3 원시값과 래퍼 객체
- 7.4 전역 객체
- 7.5 this 키워드
- 7.6 함수 호출 방식과 this 바인딩
JS가 기본으로 제공하는 빌트인 객체들과, 그 객체를 다룰 때 빠질 수 없는 this 바인딩까지 한 번에 정리해볼게.
JS의 객체는 크게 세 가지로 나뉘어. 표준 빌트인 객체는 ECMAScript 사양에 정의된 것들로 Object, String, Number, Array, Function, Promise 등 40여 개가 있어. 호스트 객체는 실행 환경(브라우저/Node.js)이 제공하는 건데 DOM, fetch, Web API 같은 거지. 그리고 사용자 정의 객체는 개발자가 직접 만든 객체야.
Math, Reflect, JSON을 제외한 표준 빌트인 객체는 모두 생성자 함수야. 인스턴스를 만들 수 있고, 프로토타입 메서드와 정적 메서드를 제공하지.
재밌는 건 원시값에서도 메서드를 쓸 수 있다는 거야. 원시값(string, number, boolean)에 마침표 표기법으로 접근하면, JS 엔진이 일시적으로 래퍼 객체를 생성하거든.
const str = 'hello';
str.toUpperCase(); // 'HELLO' — 일시적으로 String 래퍼 객체 생성
래퍼 객체 처리가 끝나면 다시 원시값으로 되돌리고, 래퍼 객체는 가비지 컬렉션 대상이 돼. 그래서 new String(), new Number() 같은 건 쓸 필요가 없어.
전역 객체는 코드 실행 전에 JS 엔진이 가장 먼저 생성하는 최상위 객체야. 브라우저에서는 window, Node.js에서는 global인데, ES11부터 globalThis로 통일됐어. 빌트인 전역 프로퍼티로 Infinity, NaN, undefined가 있고, 빌트인 전역 함수로 eval, isFinite, isNaN, parseFloat, parseInt, encodeURI/decodeURI 등이 있지. 참고로 eval은 보안과 성능 문제로 사용 금지야.
이 빌트인 객체들을 메서드로 다루다 보면 반드시 마주치는 게 this야. JS에서 가장 혼란스러운 개념 중 하나인데, 핵심은 하나지 — this는 함수가 호출되는 방식에 따라 동적으로 결정된다는 거야. 정의 시점이 아니라 호출 시점이 중요해.
함수 호출 방식에 따라 this가 어디에 바인딩되는지 하나씩 볼게. 먼저 일반 함수 호출에서는 기본적으로 **전역 객체(window/global)**에 바인딩돼. strict mode에서는 undefined이고. 중첩 함수, 콜백 함수도 일반 함수로 호출하면 전역 객체에 바인딩되니까 주의해야 해.
function foo() { console.log(this); } // window (strict: undefined)
메서드 호출에서는 메서드를 호출한 객체에 바인딩돼. 여기서 중요한 건, 메서드를 소유한 객체가 아니라 호출한 객체라는 거야.
const obj = { name: 'Lee', getName() { return this.name; } };
obj.getName(); // 'Lee' — obj가 호출했으므로 this는 obj
const other = { name: 'Kim' };
other.getName = obj.getName;
other.getName(); // 'Kim' — other가 호출했으므로 this는 other
생성자 함수 호출에서는 new로 호출하면 생성될 인스턴스에 바인딩돼.
그리고 apply/call/bind에 의한 간접 호출이 있어. apply(thisArg, [argsArray])와 call(thisArg, arg1, arg2, ...)은 함수를 호출하면서 this를 명시적으로 지정하는 거고, bind(thisArg)는 함수를 호출하지 않고 this가 바인딩된 새 함수를 반환해. 콜백 함수의 this 불일치 문제를 해결할 때 유용하지.
const person = { name: 'Lee' };
function getName() { return this.name; }
getName.call(person); // 'Lee'
getName.apply(person); // 'Lee'
getName.bind(person)(); // 'Lee'
정리
7장 읽고 기억할 거 세 가지:
- 래퍼 객체 덕분에 원시값도 객체처럼 메서드를 쓸 수 있어.
new String()같은 건 쓸 필요 없고, 전역 함수 중parseInt,isNaN정도는 실무에서 자주 쓰니까 알아둬야 해. - this는 함수가 호출되는 방식에 따라 동적으로 결정돼. 일반 함수는 전역 객체, 메서드는 호출한 객체, 생성자는 인스턴스, apply/call/bind는 첫 번째 인수 — 이 네 가지만 기억하면 돼.
- this가 헷갈리면 "누가 이 함수를 호출했는가?"를 먼저 따져봐야 해.
bind로 명시적으로 고정하는 것도 방법이고.