본문 바로가기

JavaScript

20210706 JavaSciprt DeepDive 05 : Strict mode, 암묵적 전역, 표준 빌트인 객체, 래퍼객체, 전역객체, 빌트인 전역 프로퍼티, 빌트인 전역 함수, this 키워드, this 바인딩, apply, call, bind

JavaScript Deep Dive 05



용어 및 중요사항 정리


Strict mode


  • 암묵적 전역 : 변수 키워드 선언 없이 변수에 값을 할당하는 경우, 스코프 체인에 변수가 없음에도, 암묵적으로 전역객체에 해당 변수 식별자를 프로퍼티로 동적으로 생성함
    • 암묵적 전역에 의해 전역 객체에 추가된 프로퍼티는 변수가 아니며, 호이스팅이 발생하지 않고, delete연산자로 삭제가 가능하다 (전역변수는 delete 연산자로 삭제 불가함)

  • Strict mode : 자바스크립트 언어의 문법을 좀더 엄격하게 적용하여 오류를 발생시킬 가능성이 높거나, js엔진의 최적화 작업에 문제를 일으킬수 있는 코드에 대해 명시적인 에러를 발생시킴
    • 'use strict;'를 전역 선두 또는 함수 몸체의 선두에 추가하여 적용함
      • 전역 선두에 두면 스크립트 전체에 적용
        • 스크립트 당위 적용시, 스크립트별 오류가 발생할 수 있기 때문에 즉시 실행 함수로 스크립트 전체를 감싸서 스코프를 구분하고 즉시 실행 함수의 선두에 strict mode를 적용함
      • 함수 몸체 선두에 두면 중첩 함수에 strict mode가 적용됨
  • ESLint : 린트 도구는 정적 분석기능을 통해 컴파일 상황에 소스코드를 스캔하여 문법적 오류, 잠재적 오류까지 찾아내고 오류의 원인을 리포팅해주는 유용한 도구임
    • ESLint의 경우 코드를 강제할 수 있기 때문에 더욱 강력하고, 추천함
  • ES6의 클래스, 모듈은 기본적으로 strict mode가 적용됨

  • strict mode Error
    • ReferenceError : 선언하지 않은 변수를 참조하는 경우 (암묵적 전역)
    • SyntaxError :
      • delete 연산자로 변수, 함수, 매개변수 삭제시 발생
      • 중복된 매개변수 이름사용시 발생
      • with문 사용시 발생

  • strict mode에서의 변화
    • 일반함수의 this : 일반 함수로서 호출시 this에 undefined가 바인딩 됨(일반 함수에서는 this를 사용할 필요가 없으므로), 전에는 전역객체가 바인딩 됬었음
    • arguments 객체 : 매개변수에 전달된 인수를 재할당하여 변경해도 arguments객체에 반영되지 않음



빌트인 객체


  • 자바스크립트 객체의 분류
    • 표준 빌트인 객체 : ECMAScript 사양에 정의된 객체, 애플리케이션 전역의 공통기능 제공(실행 환경 상관 없음)
    • 호스트 객체 : 자바스크립트 실행환경에서 추가로 제공하느 ㄴ객체
      • 브라우저 환경 : DOM, BOM, Canvas, XMLHttpRequest, fetch, requestAnimationFrame, SVG, Web Storage, Web Component, Web Worker등의 web API
      • Node.js 환경 : Node.js 고유의 API를 호스트 객체로 제공
    • 사용자 정의 객체:
      • 사용자가 직접 정의한 객체를 말함

  • 표준 빌트인 객체 :
    • Object, String, Number, Boolean, Symbol, Date, Math, RegExp, Array, Map/Set, WeakMap/WeakSet, Function, Promise, Reflect, Proxy, JSON, Error 등의 40여개 표준 빌트인 객체 제공
    • Math, Reflect, JSON을 제외한 표준필트인 객체는 모두 인스턴스를 생성할 수 있는 생성자 함수 객체임
    • 생성자 함수 객체인 표준 빌트인 객체 -> 프로토타입 메서드, 정적 메서드 제공
      • 해당 생성자 함수로 생성한 인스턴스의 프로타입은 표준빌트인의 프로토타입이다.
    • 생성자 함수 객체가 아닌 표준 빌트인 객체 -> 정적 메서드 제공

  • 원시값과 래퍼 객체
    • 원시값인 문자열, 숫자, 불리언 값의 경우 객체처럼 마침표 표기법 또는 대괄호 표기법으로 접근하면 JS엔진이 일시적으로 원시값을 연관된 객체로 변환해서 해당 메서드를 호출하고 다시 원시값으로 되돌림
    • 래퍼 객체(wrapper object) : 문자열, 숫자, 불리언, 심벌 값에 대해 객체처럼 접근하면 생성되는 임시 객체
      • null, undefined는 원시값임에도 불구하고 래퍼 객체를 생성하지 않음
      • 래퍼 객체가 되면 해당 타입의 생성자 함수의 인스턴스이므로 해당 표준 빌트인 객체의 프로토타입의 메서드를 상속받아 사용할 수 있음
      • 래퍼 객체의 처리가 종료되면 내부 슬롯에 할당된 원시값으로 식별자가 가지도록 되돌림
      • 래퍼 객체는 가비지 컬렉션의 대상이 되어 사라짐
      • 래퍼 객체인 상태에서 동적으로 프로퍼티를 추가해도, 처리가 끝나면 가비지 컬렉션에 의해서 래퍼 객체가 사라지기 때문에 프로퍼티의 추가는 의미가 없음

전역객체


  • 전역 객체
    • 코드가 실행되기 이전단계에 JS 엔진에 의해 어떤 객체보다도 먼저 생성되는 특수한 객체, 최상위 객체
    • 브라우저 환경 : window(self, this, frames)가 전역 객체를 가르킴
    • Node.js 환경 : global이 전역 객체를 가르킴
    • globalThis : ES11에서 실행환경 구분없이 통일한 전역객체 식별자임
    • 표준 빌트인 객체, 호스트 객체, var키워드로 선언한 전역 변수와 전역 함수, 암묵적 전역으로된 변수를 프로퍼티로 갖음
      • let, const로 선언한 변수는 전역 객체의 프로퍼티가 아님
    • 프로토타입 체인에서 최상위 객체라는 뜻이 아님, 어디에도 속하지 않고 해당 프로퍼티를 소유하는 객체일 뿐이다.
    • 전역 객체의 프로퍼티 참조시, window(global) 생략가능
    • 브라우저 환경의 모든 자바스크립트 코드는 하나의 전역 객체 window를 공유함(script 태그로 분리해놔도 공유한다는 이야기)

빌트인 전역 프로퍼티


  • 빌트인 전역 프로퍼티 : 전역 객체의 프로퍼티, 애플리케이션 전역에서 사용하는 값
    • Infinity 프로퍼티 : 무한대를 나타내는 숫자값
    • NaN 프로퍼티: 숫자가 아님을 나타내는 숫자값 (Number.NaN 프로퍼티와 같음)
    • undefined 프로퍼티 : 원시타입 undefined를 값으로 갖음

빌트인 전역 함수


  • 빌트인 전역 함수 : 전역 객체 메서드, 애플리케이션 전역에서 호출 가능한 빌트인 함수
  • eval(문자열 코드) : 자바스크립트 코드를 나타내는 문자열을 인수로 받아,
    • 해당 코드 문자열이 표현식이면 런타임에 평가하여 값생성
    • 해당 코드 문자열이 표현식이 아닌 문이라면 런타임에 실행
    • 여러개의 문으로 이루어 졌으면 모든 문을 실행하고 마지막 결과값을 반환
    • 함수와 객체 리터럴은 항상 소괄호로 감싸서 문자열을 만들 것
    • 자신이 호출된 위치에 해당하는 기존의 스코프를 런타임에 동적으로 수정함
      • strict mode에서는 기존 스코프를 수정하지 않고 자체적인 스코프를 생성함
    • eval 함수는 보안적으로 매우 취약함으로 사용하지 말자

  • isFinite() : 전달받은 인수가 정상적인 유한수인지 검사하여 유한수이면 true, 무한수이면 false반환, 숫자타입이 아닌 인수인 경우 타입 변환후 검사 수행
    • NaN으로 평가되는 경우 false 반환
  • isNaN() : 전달받은 인수가 NaN인지 검사하여 true, false 반환, 숫자타입이 아닌 인수인 경우 타입 변환후 검사 수행
    • Date는 숫자임

  • parseFloat() : 전달받은 문자열 인수를 실수로 해석하여 반환함
    • 공백은 무시됨
  • parseInt() : 전달받은 문자열 인수를 선택한 특정 진법으로 해석하여 10진수 정수로 반환
    • parseInt(string, 해석 기준 진법 number), default는 10진수로 해석(16진수 리터럴 코드만 알아서 해석함)
    • string에 있는 앞뒤 공백은 무시되나, 중간에 껴있는 공백은 공백까지만 해석하고 이후로는 무시함
    • string이 해당 해석 기준 진법에는 없는 문자가 있는 경우, 그것을 포함한 이후는 모두 무시 됨
  • 빌트인 전역함수는 아니지만 Number.prototype.toString 메서드를 활용하면 10진수를 원하는 진수로 변환하여 그결과를 문자열로 반환 받을 수 있음
    • toString(변환 기준 진법 number) -> string

  • encodeURI() 함수 : 완전한 URI(Uniform Resource Idenifier)를 문자열로 받아 이스케이프 처리를 위해 인코딩함 (쿼리스트링의 구분자 (?, =, &) 기호는 이스케이프 처리에서 제외됨)
  • decodeURI() 함수 : 인코딩된 URI를 인수로 전달 받아 이스케이프 처리 이전으로 디코딩 함
    • URI : 인터넷 자원을 나타내는 유일한 주소로 하위 개념으로 URL, URN이 있음
      • URL : Scheme(Protocol), Host(Domain), Port, Path 까지
        • URL는 아스키 문자셋으로만 구성되어야 함
        • 한글, 공백, 특수문자, 외국어 등이 올수 없어 이스케이프 처리되어서 들어와야됨
      • URN : Host(Domain), Port, Path, Query(Qeury String), Fragment 까지
      • URI는 URL, URN을 합친 것을 말함
    • 이스케이프 처리 : 네트워크를 통해 정보를 공유할때 어떤 시스템에서도 읽을 수 있는 아스키 문자셋으로 변환하는 것
      • 알파벳, 09의 숫자, _-.!*'() 문자는 이스케이프 처리에서 제외됨

  • encodeURIComponent() 함수 : URI 구성요소를 인수로 전달받아 인코딩(이스케이프 처리)함
    • 전달된 문자열을 URI 구성요소인 쿼리 스트링의 일부로 간주하여 쿼리스트링 구분자 (=, ?, &)기호 까지 이스케이프 처리하여 인코딩함
  • decodeURIComponent() 함수 : 전달된 URI 구성 요소를 디코딩 함



this 키워드


  • 메서드는 자신이 속한 객체의 상태(프로퍼티)를 참조하고 변경하기 위해서는 자신이 속한 객체를 가리키는 식별자를 참조할 수 있어야 함
  • 객체 리터럴 방식으로 생성한 객체의 경우, 자신이 속한 객체를 가르키는 식별자를 재귀적으로 참조할 수 있음
    • 메서드가 호출되는 시점에는 이미 객체리터럴 평가가 완료되어 객체가 생성 되었기 때문에 재귀적으로 자신을 호출 가능함 (this 방식으로도 가능함, 이때는 this가 객체 자신을 의미함으로)
  • 생성자 함수 방식으로 인스턴스를 생성하는 경우, 인스턴스의 식별자가 정해지지 않은 상태라서 재귀적으로 호출할 수 없으며, 인스턴스 마다 식별자가 다르기 때문에 더더욱 재귀적으로 인스턴스의 식별자로 참조할 수는 없음 -> 그래서 특수한 식별자 this를 활용함

  • this : 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수이다.
    • JS 엔진에 의해 암묵적으로 생성되고, 코드 어디서든 참조 가능함
    • 바인딩 : 식별자와 값을 연결하는 과정
    • this 바인딩 : this가 가르키는 값을 this와 연결(바인딩)하는 것으로 함수 호출 방식에 의해 동적으로 결정 됨
  • 렉시컬 스코프 vs this 바인딩 결정 시점
    • 렉시컬 스코프 : 함수정의가 평가되어 함수 객체가 생성되는 시점
    • this 바인딩 : 함수 호출 시점

함수 호출 방식에 따른 this 바인딩


  • 일반함수 호출 : window (global 전역객체)
    • strict mode인 경우 : undefined

  • 객체.메서드 호출 : 메서드를 소유한 객체가 아니라 메서드를 호출한 객체
    • 해당 메서드는 단지 별도의 this가 사용된 함수객체를 가르키고 있을 뿐이라서 어디든지 할당되어 옴겨질수 있기 때문
    • 일반 변수에 할당되어 일반 함수로 호출되면 this는 window를 가르킴
    • 프로토타입 객체에 this 가 사용된 메서드가 정의 되어 인스턴스에서도 해당 메서드를 활용하게 되면, 프로토타입에서 사용하면 프로토타입이 this, 인스턴스에서 사용하게 되면 인스턴스가 this

  • new 생성자 함수 호출 : 생성자 함수가 생성할 인스턴스
    • new 없이 생성자 함수를 호출하는 경우 일반 함수로 동작

  • Function.prototype.apply/call/bind 메서드에 의한 간접 호출
    • : 메서드에 첫번째 인수로 전달한 객체
    • Function.prototype의 메서드로서 모든 함수가 상속받아 사용할 수 있음
    • apply, call 메서드 : this로 사용할 객체와 인수 리스트를 인수로 받아 함수 호출
      • 첫번째 인수 : this로 사용할 객체 -> this 바인딩,
      • 두번째 인수 : 인수 리스트 -> 본래 함수의 인수 arguments에 넣어줌
      • apply vs call :
        • apply(thisArg, [1, 2, 3, ...]) 배열이 들어감
        • call(thisArg, 1, 2, 3, ...) 쉼표로 나열하며 계속 인수가 들어감
        • 보통 유사배열 객체에 배열 메서드 사용하게 하는 경우에 사용됨
    • bind 메서드 : 함수 호출하지 않고 this로 사용할 객체만 전달함
function convertArgsToArray() {
  const arr = Array.prototype.slice.call(arguments);
  return arr;
}
convertArgsToArray(1, 2, 3); // [1, 2, 3]

  • 어디서든지 일반함수로 호출되는 모든 함수(중첩함수, 콜백함수 포함): window
    • window로 바인딩 되면, 객체의 프로퍼티 값(상태값)를 변경시키기가 어려워짐
    • 메서드 내부의 중첩함수, 콜백함수의 this 바인딩 메서드의 this와 일치시키는 법
      • 방법01 : 콜백함수, 중첩함수 밖에서 this를 변수에 담아서 콜백, 중첩함수 안에서 사용하는 방법
      • 방법02 : Function.prototype.apply/call/bind 메서드를 활용하여 명시적으로 바인딩 시킴
      • 방법03 : 화살표함수 내부에서 this를 사용하기 (화살표 함수의 this는 상위 스코프의 this를 가리킴)